home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PCMania 30
/
PCMania CD30.iso
/
pcmania
/
polyray
/
polyray.doc
< prev
next >
Wrap
Text File
|
1994-03-18
|
203KB
|
4,862 lines
Polyray v1.7
(c) 1991-1994 Alexander R. Enzmann
17 March 1994
All Rights Reserved
1 Introduction & Shareware Information 1
1.1 Acknowledgments and Other Legal Stuff 2
1.2 Useful tools 3
1.2.1 Animation Utilities 3
1.2.2 Modellers 4
1.2.3 Miscellaneous Programs 4
1.2.4 Other Raytracers 5
1.2.5 References 5
1.3 Contents of this document 6
1.4 Quick demo 6
1.5 Command Line Options 8
1.6 Initialization File 9
1.7 Rendering Options 10
1.7.1 Raytracing 10
1.7.1.1 Antialiasing 11
1.7.1.2 Focal Blur 12
1.7.1.3 Bounding Slabs 12
1.7.1.4 Shading Quality Flags 13
1.7.2 Scan Conversion 14
1.7.3 Wireframe 15
1.7.4 Depth Files 15
1.7.5 Raw Triangles 16
1.8 Display Options (IBM format only) 16
2 Detailed description of the Polyray input format 17
2.1 Expressions 18
2.1.1 Numeric expressions 18
2.1.2 Vector Expressions 19
2.1.3 Arrays 20
2.1.4 Conditional Expressions 22
2.1.5 Run-Time expressions 22
2.1.6 Named Expressions 23
2.1.6.1 Static variables 24
2.1.6.2 Lazy Evaluation 25
2.2 Definition of the viewpoint 26
2.3 Objects/Surfaces 28
2.3.1 Object Modifiers 28
2.3.1.1 Position and Orientation Modifiers 29
2.3.1.1.1 Translation 29
2.3.1.1.2 Rotation 29
2.3.1.1.3 Scaling 29
2.3.1.1.4 Shear 30
2.3.1.1.5 Displace 30
2.3.1.1.6 UV Bounds 31
2.3.1.2 Bounding box 31
2.3.1.3 Subdivision of Primitives 32
2.3.1.4 Shading Flags 32
2.3.2 Primitives 33
2.3.2.1 Bezier patches 33
2.3.2.2 Blob 34
2.3.2.3 Box 35
2.3.2.4 Cone 36
2.3.2.5 Cylinder 36
2.3.2.6 Disc 36
2.3.2.7 Glyphs (TrueType fonts) 37
2.3.2.8 Implicit Surface 38
2.3.2.9 Height Field 39
2.3.2.9.1 File Based Height Fields 40
2.3.2.9.1.1 8 Bit Format 40
2.3.2.9.1.2 16 Bit Format 40
2.3.2.9.1.3 24 Bit Format 41
2.3.2.9.1.4 32 Bit Format 42
2.3.2.9.2 Implicit Height Fields 42
2.3.2.10 Lathe surfaces 43
2.3.2.11 NURBS 44
2.3.2.12 Parabola 45
2.3.2.13 Parametric surface 46
2.3.2.14 Polygon 46
2.3.2.15 Polynomial surface 47
2.3.2.16 Spheres 48
2.3.2.17 Sweep surface 48
2.3.2.18 Torus 49
2.3.2.19 Triangular patches 50
2.3.3 Constructive Solid Geometry (CSG) 50
2.3.4 Gridded objects 51
2.3.5 Particle Systems 53
2.4 Color and lighting 54
2.4.1 Light sources 55
2.4.1.1 Positional Lights 55
2.4.1.2 Spot Lights 55
2.4.1.3 Directional lights 56
2.4.1.4 Textured lights 56
2.4.1.4.1 Area Lights 56
2.4.1.5 Depth Mapped Lights 57
2.4.2 Background color 57
2.4.2.1 Global Haze (fog) 59
2.4.3 Textures 59
2.4.3.1 Procedural Textures 59
2.4.3.1.1 Standard Shading Model 60
2.4.3.1.1.1 Ambient light 61
2.4.3.1.1.2 Diffuse light 61
2.4.3.1.1.3 Specular highlights 62
2.4.3.1.1.4 Reflected light 62
2.4.3.1.1.5 Transmitted light 62
2.4.3.1.1.6 Microfacet distribution 63
2.4.3.1.2 Checker 63
2.4.3.1.3 Hexagon 64
2.4.3.1.4 Noise surfaces 64
2.4.3.2 Functional Textures 67
2.4.3.2.1 Color maps 68
2.4.3.2.1.1 Using CMAPPER 69
2.4.3.2.2 Image maps 69
2.4.3.2.3 Bumpmaps 71
2.4.3.3 Indexed Textures and Texture Maps 72
2.4.3.4 Layered Textures 73
2.4.3.5 Summed Textures 74
2.5 Comments 74
2.6 Animation support 74
2.7 Conditional processing 75
2.8 Include files 76
2.9 File flush 76
2.10 System calls 76
3 File formats 77
3.1 Output files 77
4 Outstanding Issues 77
4.1 To do list 77
4.2 Known Bugs 77
5 Revision History 78
1
1 Introduction & Shareware Information
The program Polyray is a rendering program for producing scenes of 3D
shapes and surfaces. The means of description range from standard
primitives like box, sphere, etc. to 3 variable polynomial expression,
and finally (and slowest of all) surfaces containing transcendental
functions like sin, cos, log. The files associated with Polyray are
distributed in three pieces: the executable, a collection of document
files, and a collection of data files.
Version 1.5 and later of Polyray are Shareware. If you enjoy this
program, use it frequently, and can afford to pay a registration fee,
then send $35 to:
Alexander Enzmann
20 Clinton St.
Woburn, Ma 01801
USA
Please include your name and mailing address.
If you formally register this program, you will receive free the next
release of Polyray, when it occurs. In addition you will be
contributing to my ability to purchase software tools to make Polyray
a better program. If you don't register this program, don't feel bad
- I'm poor too - but you also shouldn't expect as prompt a response to
questions or bugs. Consider it guilt-free Shareware. Note that all
of the sample files in PLYDAT are Public Domain, you may use them
freely.
It is assumed that you are familiar with files compressed with PKZIP
(version 2.04 is required) and setting the PATH variable to make an
executable visible to DOS. If you aren't, then get some help from
someone who has used ZIP and is familiar with configuring a DOS
system.
The data files are ASCII text, the output image file format supported
is Targa. Input images (for texturing, height fields, etc.) may be:
Targa (all variants defined in the Truevision spec), GIF (both 87a and
89a should work), or JPEG (JFIF format). The Targa format is
supported by many image processing programs so if you need to
translate between Targa and something else it is quite simple. The
utility CJPEG, which converts from Targa to JPEG has been included in
the utility archive. Polyray is case sensitive, so the following ways
of writing foo are all considered different: Foo, FOO, and foo. For
an abbreviated list of Polyray's syntax, see the file quickref.txt.
Polyray supports a number of VESA compliant display modes from the
standard 320x200 VGA mode through to 24 bits at high resolution. If
you don't have a VESA compliant graphics board you will either be
limited to standard VGA or you will need to find a VESA driver for
your board.
The executable requires IBM PC compatible with at least a 386 and 387,
2
and a minimum of 2 Mbytes of RAM to run. Other memory models will be
made available if enough requests are made. The distributed
executable uses a DOS extender in order to grab as much memory as is
available. It has been successfully run with both HIMEM.SYS, and
386MAX.SYS. Under Windows it will run in a DOS box, however if you
use the graphics display it will need to be run full screen.
There have been a number of problems reported of conflicts between the
DOS extender used in Polyray and memory managers. These all involve
DOS 6.0 or later. In general the conflict results in an error message
along the lines of:
Previously loaded software is neither VCPI or DPMI compliant
If you see this message, or one like it, you may need to disable EMS
handling from you CONFIG.SYS file. A typical change is:
EMM386 NOEMS
For some people, just running Polyray in a DOS box under Windows is
sufficient to eliminate memory manager problems. As a first step in
resolving this type of problem, try setting up a CONFIG.SYS and
AUTOEXEC.BAT with as few entries as possible.
I'm interested in any comments/bug reports. I can be reached via
email by:
Compuserve as: Alexander Enzmann 70323,2461
Internet as: xander@mitre.org
1.1 Acknowledgments and Other Legal Stuff
There is no warranty, explicit or implied, of the suitability of this
software for any purpose. This software is provided as is, and the
user of this software assumes the entire risk as to its quality,
accuracy, and for any damages resulting from the use of this software.
No part of this package may be included as part of a commercial
package without explicit written permission.
This original code for this program was based on the mtv ray-tracer,
written (and placed in the public domain) by Mark VandeWettering.
This software is based in part on the work of the Independent JPEG
Group.
The Graphics Interchange Format(c) is the copyright property of
Compuserve Incorporated. The service marks Graphics Interchange
Format(sm), and GIF(sm) are owned by Compuserve Incorporated.
Many thanks go to David Mason for his numerous comments and
suggestions (and for adding a new feature to DTA every time I needed
3
to test something). Thanks also to the Cafe Pamplona in Cambridge Ma.
for providing a place to get heavily caffinated and rap about ray-
tracing and image processing for hours at a time.
Kudos go to Jeff Bowermaster, Alfonso Hermida, and Dan Richardson for
extensive beta testing of Polyray and their many suggestions for
improvements. Additionally, many thanks to the folks that let me
include some of their scenes in the data archive: Jeff Bowermaster,
Dan Farmer, and Will Wagner.
1.2 Useful tools
There are several types of tools that you will want to have to work
with this program (in relative order of importance):
o An ASCII text editor for preparing and modifying input files.
o A picture viewer for viewing images.
o An image processing program for translation between Targa and
some other format.
o An animation generator that will take a series of Targa images
and produce an animation file.
o An animation viewer for playing an animation on the screen.
There are a number of tools out there that can be used in conjunction
with Polyray. I've somewhat arbitrarily divided them into categories,
with no particular order of importance. I'm pretty sure all of the
following are available from Compuserve in either the GRAPHDEV or
GRAPHSUP forums.
1.2.1 Animation Utilities
Many of the features in Polyray are specifically to support the
generation of multiple frames. Once the frames are generated,
additional tools are necessary to format and play the resulting
animation. Top picks in this area are:
o DTA (David Mason)
o DFV (David Mason)
o PLAY (Trilobyte)
Dave's Targa Animator (DTA) may be the only tool you need for creating
FLI/FLCs from images, converting images between formats, compositing
images, etc. For building animations with Polyray, all you need is
DTA and a FLI/FLC player.
DFV and PLAY80 do a single thing. They play FLI/FLC animations on a
VGA or SVGA screen. Either one will do the job for 8 bit FLI and FLC
animations. However, DFV can handle a few formats that aren't
supported by PLAY (in particular the 16 and 24 bit animation formats
4
produced by DTA).
1.2.2 Modellers
There are a couple of really good Shareware modellers out there. Both
of them allow the creation of objects in native form (rather than as
hundreds of polygons), and provide fast, interactive, development of
scenes using wireframe displays. The modellers are:
o POV-CAD (Alphonso Hermida)
o Moray (Lutz Kretzschmar)
POV-CAD, written by Alfonso Hermida is a graphical modeling program
with both Windows and DOS versions. POV-CAD allows you to quickly
build models through point and click in a wireframe environment. Both
Polyray and POV-Ray data files can be generated.
Actually, Moray is a modeller for POV-Ray, but it is mentioned here
since it is a very good Shareware modeller. Perhaps if enough people
ask Lutz to add Polyray support...
1.2.3 Miscellaneous Programs
o CTDS (Truman Brown)
o RAW2POV (Steve Anger)
o 3DS2POV (Steve Anger)
o SPD (Eric Haines,
updates by Eduard Schwan & Alexander Enzmann)
CTDS is dot-to-dot for renderers. By creating a file listing lots of
points and associated radii, this program will connect them together
using spheres and cones. It can output to the native input of several
renderers, including: Polyray, POV-Ray, and Vivid. This programs
really fun for creating abstract shapes.
RAW2POV is a conversion program that takes ASCII files of triangle
vertices and creates an input for various renderers. It can also
examine the triangles and calculate normals for the vertices, giving a
very smooth looking object when rendered. This is a must have utility
if you are going to be converting polygonal objects into a form that
can be used by Polyray.
3DS2POV converts model files built by the costly 3DS modeller/renderer
into the native input of various renderers. Really nice to have if
you are getting models in 3DS format or if you are fortunate to have
3DS yourself.
SPD is a library of C code originally developed to benchmark various
renderers. Similar in nature to OORT (described below), it allows you
to write short C programs that will generate scene files for a number
of different renderers. Currently supported are: Polyray, POV-Ray,
Vivid, Rayshade, Rend386, RTrace, NFF, and QRT. There are also
routines for displaying the models in wireframe (have to be tuned for
individual compilers and platforms). New output modes and rendering
5
formats are still being added.
1.2.4 Other Raytracers
If you want to explore the world of raytracers, below is a list of
what I consider the best of the Shareware/Freeware world. (No flame
wars please, these are just ones I happen to have and like.) Each has
it's own strengths and weaknesses. Overall, you will learn something
from working with each one of them.
o POV-Ray (The POV-Ray Team)
o Vivid (Stephen Coy)
o Rayshade (Craig Kolb)
o OORT (Nicholas Wilt)
POV-Ray is the camel that you let warm it's nose in your tent. It's
starting to really dominate the scene. It's strengths are in a broad
set of features together with a huge level of support. The authors
are available on Compuserve (Graphdev) to answer questions, there are
an astonishing number of utilities, and the source code is available
for porting it to different platforms.
For a really fast raytracer, Vivid can't be beat. It's DOS based
only, but the registered version comes with a DOS extender for those
really huge scene files. Shape primitives are somewhat limited, but
the texturing capabilities and camera lens features are very strong.
Rayshade is an old standard for the UNIX workstation crowd. It has
just about all of the shapes you would want, texturing approximately
on a par with POV-Ray, and an interesting gridded optimization
technique. It doesn't do graphics, and it's really slow if you don't
manually tune the optimization. On the plus side it has a long
background of people using it and will compile on just about anything
(source code is available).
A real newcomer, OORT is a whole bunch of C++ code (classes, objects,
whatever) for creating and then rendering scenes. No native input
language yet (other than minimal NFF support), so you really need a
C++ compiler to use it. (This is likely to change as Nicholas is
really energetic and seems to want to throw just about everything into
OORT.)
1.2.5 References
Over the last year or so there have been several books published that
describe ways to use Polyray. Although these books were written about
v1.6a, almost all of the content is still valid for v1.7. The
differences are pretty well described in section 5. Other differences
are buried here and there in this document. (I don't remember what
they all are, so I won't try to list them.)
Of the books published, the three that really dig into using Polyray
for various tasks, from modeling to animations are:
6
Making Movies on Your PC
David Mason & Alexander Enzmann
Waite Group Press, 1993
ISBN 1-878739-41-7
Adventures in Ray Tracing
Alphonso Hermida
Que, 1993
ISBN 1-56529-555-2
Animation How-To CD
Jeff Bowermaster
Waite Group Press, 1994
ISBN 1-878739-54-9
There's good stuff in each of these books, so it's not unreasonable to
actually get all of them. (I don't get royalties from any of them, so
this is a reasonably shameless plug.)
1.3 Contents of this document
This document describes the input format and capabilities of the
Polyray ray-tracing program. The following features are supported:
o Viewpoint (camera) characteristics,
o Positional (point), directional (spotlight) light sources, and
functional (textured) lights.
o Background color,
o Shape primitives:
Bezier patch, blob, box, cone, cylinder, disc, glyph,
implicit function, height field, lathe surface, NURBS,
parabola, parametric function, polygon, polynomial
function, sphere, sweep surface, torus, triangular patches
o Animation support,
o Conditional processing,
o Include files,
o Named values and objects.
o Constructive Solid Geometry (CSG)
o Grids of objects
o User definable (functional) textures
o Initialization file for default values
1.4 Quick demo
This section describes one of the simplest possible data files: a
single sphere and a single light source. In what follows anything
following a double slash, // is a comment and is ignored by Polyray.
The data file is the following indented lines. You can either use the
file sphere.pi in the data archive, or copy these declarations into a
new file.
7
// We start by setting up the camera. This is where the eye is
// located, and describes how wide a field of view will be used,
// and the resolution (# of pixels wide and high) of the resulting
// image.
viewpoint {
from <0,0,-8> // The location of the eye
at <0,0,0> // The point that we are looking at
up <0,1,0> // The direction that will be up
angle 45 // The vertical field of view
resolution 160, 160 // The image will be 160x160 pixels
}
// Next define the color of the background. This is the color
// that is used for any pixel that isn't part of an object. In
// this file it will color the image around the sphere.
background skyblue
// Next is a light source. This is a point light source a little
// above, to the left, and behind the eye.
light <-10,3, -20>
// The following declaration is a texture that will be applied
// to the sphere. It will be red at every point but where the
// light is reflecting directly towards the eye - at that point
// there will be a bright white spot.
define shiny_red
texture {
surface {
color red
ambient 0.2 // Always a little red in the sphere
diffuse 0.6 // Where the light is hitting the
// sphere, the color of the sphere
// will be a little brighter.
specular white, 0.5 // There will be a white highlight.
microfacet Cook 5 // The white highlight drops to half
// its intensity at an angle of 5
// degrees.
}
}
// Finally we declare the sphere. It sits right at the origin,
// and has a radius of two. Following the declaration of the
// sphere, we associate the texture declared above.
object {
sphere <0, 0, 0>, 2
shiny_red
}
Now that we have a data file, lets render it and show it on the
screen. First of all we can look at a wireframe representation of the
file. Enter the following commands (DOS prompts are in capitals).
C> polyray sphere.pi -r 2 -V 1 -W
8
An outline of the sphere will be drawn on the screen, press any key to
get back to DOS. Next lets do a raytrace. Enter the following:
C> polyray sphere.pi -V 1
The sphere will be drawn a line at a time, when it is done you will be
returned to DOS
The output image will be the default output file, out.tga. You can
view it directly with VPIC or CSHOW (although VPIC will not have the
full range of colors that were generated by the raytrace). If you
have PICLAB, then the following commands will load the image, map its
colors into a spectrum that matches the colors in the image, then will
display it.
C:> piclab
> tload out
> makepal
> map
> show
hit any key to get back to PICLAB's command line
> quit
C:>
You should see a greatly improved image quality over the display that
is shown during tracing.
Now that you have seen a simple image, you have two options: go render
some or all of the files in the data archive, PLYDAT, or continue
reading the documents. For those of you that prefer immediately
getting started, there are a series of DOS batch files that pretty
well automate the rendering of the sample scenes. (When you unzip the
data files, remember to use the -d switch so that all the
subdirectories will be installed properly.) The sample scenes in
PLYDAT will render on a 33Mhz 486 in less than a day. The animation
examples will take an additional 4-5 days (and chew up quite a few
megabytes of disk space).
1.5 Command Line Options
A number of operations can be manipulated through values in an
initialization file, within the data file, or from the command line
(processed in that order, with the last read having the highest
precedence). The command line values will be displayed if no
arguments are given to Polyray.
The values that can be specified at the command line, with a brief
description of their meaning are:
-a n Antialiasing (0=none, 1=filter, 2-4=adaptive)
-b pixels Set the maximum number of pixels that will be
9
calculated between file flushes
-B Flush the output file every scan line
-d Output the image as a depth map
-o filename The output file name (if not specified is out.tga)
-p bits/pixel Set the number of bits per pixel in the output file
(must be one of 8, 16, 24, 32)
-P pallette Which pallette option to use in 8 bit display modes
[0=grey, 1=666, 2=884]
-Q Abort if any key is hit during trace
-q flags Turn on/off various global shading options
-r renderer Which rendering method: [0=raytrace, 1=scan convert,
2=wireframe, 3=raw tri, 4=u/v tri]
-R Resume an interrupted trace.
-S samples # of samples per pixel when performing focal blur
-t status_vals Status display type [0=none, 1=totals, 2=line,
3=pixel].
-T threshold Threshold to start adaptive antialiasing
-u Write the output file in uncompressed form
-v Trace from bottom to top
-V mode Use VGA display while tracing [0=none,1-5=8 bit,
6-10=15 bit, 11-15=24 bit]
-W Wait for key before clearing display
-x columns Set the x resolution
-y lines Set the y resolution
-z start_line Start a trace at a specified line
If no arguments are given then Polyray will give a brief description
of the command line options.
1.6 Initialization File
The first operation carried out by Polyray is to read the
initialization file polyray.ini. This file can be used to tune a
number of the default variables used during rendering. It must appear
in the current directory. It doesn't have to exist, it is typically
used as a convenience to eliminate retyping command line parameters.
Each entry in the initialization file must appear on a separate line,
and have the form:
default_name default_value
The names are text. The values are numeric for some names, and text
for others. The allowed names and values are:
abort_test true/false/on/off
alias_threshold [threshold to start adaptive antialiasing]
antialias none/filter/adaptive1/adaptive2
display none/vga1-vga5/hicolor1-hicolor5/
truecolor1-truecolor5
max_level [max depth of recursion]
max_samples [# samples for focal blur]
optimizer none/slabs
10
pixel_size [8, 16, 24, 32]
pixel_encoding none/rle
renderer ray_trace/scan_convert/wire_frame/
raw_triangles/uv_triangles
shade_flags [default/bit mask of flags, (see section
1.7.1.4)]
shadow_tolerance [minimum distance for blocking objects]
status none/totals/line/pixel
warnings on/off
A typical example of polyray.ini would be:
abort_test on
alias_threshold 0.05
antialias adaptive
display vga
max_samples 8
pixel_size 24
status line
If no initialization file exists, then Polyray will use the following
default values:
abort_test on
alias_threshold 0.2
antialias none
display none
max_level 5
max_samples 4
optimizer slabs
pixel_size 16
pixel_encoding rle
renderer ray_trace
shade_flags default
shadow_tolerance 0.001
status none
warnings on
1.7 Rendering Options
Polyray supports four very distinct means of rendering scenes:
raytracing, polygon scan conversion, wireframe, and raw triangle
output. Raytracing is often a very time consuming process, however
the highest quality of images can be produced this way. Scan
conversion is a very memory intensive method, but produces a good
quality image quickly. Wireframe gives a very rough view of the scene
in the fastest possible time. (Note that there is no output file when
wireframe is used.) Raw triangle output produces an ASCII file of
triangles describing the scene.
1.7.1 Raytracing
11
Raytracing is a very compute intensive process, but is the method of
choice for a final image. The quality of the images that can be
produced is entirely a function of how long you want to wait for
results. There are certain options that allow you to adjust how you
want to make tradeoffs between: quality, speed, and memory
requirements.
The basic operation in raytracing is that of shooting a ray from the
eye, through a pixel, and finally hitting an object. For each type of
primitive there is specialized code that helps determine if a ray has
hit that primitive. The standard way that Polyray generates an image
is to use one ray per pixel and to test that ray against all of the
objects in the data file. Antialiasing or focal blur will result in
more than one ray per pixel, increasing rendering time.
1.7.1.1 Antialiasing
The representation of rays is as a 1 dimensional line. On the other
hand pixels and objects have a definite size. This can lead to a
problem known as aliasing, where a ray may not intersect an object
because the object only partially overlaps a pixel, or the pixel
should have color contributed by several objects that overlap it, none
of which completely fills the pixel. Aliasing often shows up as a
staircase effect on the edges of objects.
Polyray offers a couple of ways to reduce aliasing: by filtering or by
adaptive oversampling. Filtering smoothes the output image by
averaging the color values of neighboring pixels to the pixel being
rendered. Oversampling is performed by adding extra rays around the
original one. By averaging the result of all of the rays that are
shot through a single pixel, aliasing problems can be greatly reduced.
The filtering process adds little overhead to the rendering process,
however the resolution of the image is degraded by the averaging
process. It simply averages the colors found at the four corners of a
pixel.
Adaptive antialiasing starts by sending a ray through the four corners
of a pixel. If there is a sufficient contrast between the corners
then Polyray will fire five more rays within the pixel. This results
in four subpixels. Each level of adaptive antialiasing repeats this
procedure. If adaptive1 is used in polyray.ini, or -a 2 from the
command line, then the antialiasing stops after a single subdivision.
If adaptive2 is used then each subpixel is checked and if the corners
are sufficiently different then it will be divided again.
The two initialization (and command line) variables that will affect
the performance of adaptive antialiasing are alias_threshold and
max_samples. The first is a measure of how different a pixel must be
with respect to its neighbors before antialiasing will kick in. If a
pixel has the value: <r1, g1, b1>, and it's neighbor has the value
<r2, g2, b2> (RGB values between 0 and 1 inclusive), then the distance
between the two colors is:
12
dist = sqrt((r1 - r2)^2 + (b1 - b2)^2 + (g1 - g2)^2)
This is the standard Pythagorean formula for values specified in RGB.
If dist is greater than the value of alias_threshold, then extra rays
will be fired through the pixel in order to determine an averaged
color for that pixel.
1.7.1.2 Focal Blur
The default viewpoint declaration is a pinhole camera model. All
objects are in sharp focus, no matter how near or far they are. By
adjusting the aperture and focal_distance parameters it is possible to
simulate a real world camera. Objects close to the focal distance
will be in focus and those closer or farther will be blurred. Three
parameters affect the blur: aperture, focal_distance, and max_samples.
Adjusting the size of aperture allows you to adjust how much of the
scene is in focus. Adjusting focal_distance gives you control over
what part of the scene is in focus. If focal_distance isn't set,
Polyray will set it so that the point defined by the 'at' declaration
is in focus. The declaration max_samples allows you to fine tune how
smooth the final image appears. If you use a large value for
aperture, you will probably need to increase max_samples as well to
avoid a grainy appearance in the image.
Also note that focal blur and adaptive antialiasing interact with each
other. Polyray attempts to distribute the focal blur rays into the
antialiasing rays, however there will be more rays cast than would
have been using either method alone. It's best to leave the
antialiasing off and the value of max_samples small until producing
the final image.
1.7.1.3 Bounding Slabs
For scenes with large numbers of small objects, there are optimization
tricks that can take advantage of the distribution of the objects. An
easy one to implement, and one that often results in huge time savings
are bounding slabs. The basic idea of a bounding slab is to sort all
objects along a particular direction. Once this sorting step has been
performed, then during rendering the ray can be quickly checked
against a slab (which can represent many objects rather than just one)
to see if intersection tests need to be made on the contents of the
slab.
The most important caveats with Polyray's implementation of bounding
slabs are:
o Scenes with only a few large objects will derive little speed
benefits.
o If there is a lot of overlap of the positions of the objects in
the scene, then there will be little advantage to the slabs.
o If the direction of the slabs does not correspond to a direction
13
that easily sorts objects, then there will be little speed
gained.
However, for data files that are generated by another program, the
requirements for effective use of bounding slabs are often met. For
example, most of the data files generated by the SPD programs will be
rendered orders of magnitude faster with bounding slabs than without.
Slabs are turned on by default. To turn them off either use the
command line flag, -O 0, or add the line, optimizer none, to
polyray.ini.
1.7.1.4 Shading Quality Flags
By specifying a series of bits, various shading options can be turned
on and off. The value of each flag, as well as the meaning are:
1 Shadow_Check Shadows will be generated
2 Reflect_Check Reflectivity will be tested
4 Transmit_Check Check for refraction
8 Two_Sides If on, highlighting will be performed
on both sides of a surface.
16 UV_Check Calculate the uv-coordinates of each point
32 Cast_Shadow Determines if an object can cast a shadow
The default settings of these flags are:
raytracing Shadow_Check + Reflect_Check + Transmit_Check +
UV_Check (= 23)
scan conversion Two_Sides (= 8)
wireframe Not applicable
raw triangles Not applicable
Note that the flag, cast_shadow, is only meaningful for declarations
of shading flags for an object, not for the renderer. See section
2.3.1.4 for more information. If you want to turn off shadows in a
raytrace, then you would leave out the shadow_check flag (conversely
if you wanted shadows in scan conversion you would add it).
The assumption made is that during raytracing the highest quality is
desired, and consequently every shading test is made. The assumption
for scan conversion is that you are trying to render quickly, and
hence most of the complex shading options are turned off. In some
images the quality will be improved if you
If any of the three flags Shadow_Check, Reflect_Check, or
Transmit_Check are explicitly set and scan conversion is used to
render the scene, then at every pixel that is displayed, a recursive
call to the raytracer will be made to determine the correct shading.
Note that due to the faceted nature of objects during scan conversion,
shadowing, and especially refraction can get messed up during scan
conversion.
14
For example if you want to do a scan conversion that contains shadows,
you would use the following:
polyray xxx.pi -r 1 -q 1
or if you wanted to do raytracing with no shadows, reflection turned
on, transparency turned off, and with diffuse and specular shading
performed for both sides of surfaces you would use options 2 and 8:
polyray xxx.pi -q 10
Note: texturing cannot be turned off.
1.7.2 Scan Conversion
In order to support a quicker render of images, Polyray can render
most primitives as a series of polygons. Each polygon is scan
converted using a Z-Buffer for depth information, and a S-Buffer for
color information.
The scan conversion process does not by default provide support for:
shadows, reflectivity, or transparency. It is possible to instruct
Polyray to use raytracing to perform these shading operations through
the use of either the global shade flags or by setting shade flags for
a specific object. An alternative method for quickly testing shadows
involves using depth mapped lights (see section 2.4.1.5). Reflections
can be simulated with a multipass approach where an image map or
environment map is calculated and then applied to the reflective
object.
The memory requirements for performing scan conversion can be quite
substantial. You need at least as much memory as is required for
raytracing, plus at least 7 bytes per pixel for the final image. In
order to correctly keep track of depth and color - 4 bytes are used
for the depth of each pixel in the Z-Buffer (32 bit floating point
number), and 3 bytes are used for each pixel in the S-Buffer (1 byte
each for red, green, and blue).
During scan conversion, the number of polygons used to cover the
surface of a primitive is controlled using the keywords u_steps, and
v_steps (or combined with uv_steps). These two values control how
many steps around and along the surface of the object values are
generated to create polygons (see section 2.3.1.1.6). The higher the
number of steps, the smoother the final appearance. Note however that
if the object is very small then there is little reason to use a fine
mesh - hand tuning is sometimes required to get the best balance
between speed and appearance.
Generating isosurfaces for blobs, polynomial functions and implicit
functions, followed by polygonalization of the surfaces is performed
using a marching cubes algorithm. Currently the value of u_steps
determines the number of slices along the x-axis, the value of v_steps
controls the number of slices along the y-axis, and the value of
15
w_steps controls the number of slices along the z-axis. Future
versions may introduce a third value to allow independent control of y
and z.
Note that running Polyray in a Windows DOS box is a way to increase
the memory available for performing scan conversion. Polyray will
take advantage of the virtual memory capabilities of Windows, allowing
for larger image to be rendered.
Also note that there are images that render slower in scan conversion
and wireframe that raytracing! These are typically those files that
contain large numbers of spheres and cones such as data files
generated by CTDS or large gridded objects. The scan conversion
process generates every possible part of each object, whereas the
raytracer is able to sort the objects an only display the visible
parts of the surfaces. If you run into this situation, a speedup
trick you can use is to set the values of uv_steps very low for the
small objects (e.g., uv_steps 6, 4 for spheres).
1.7.3 Wireframe
In most cases the fastest way to display a model is by showing a
wireframe representation. Polyray supports this as a variant of the
scan conversion process. When drawing a wireframe image only the
edges of the polygons that are generated as part of scan conversion
are drawn onto the screen. A big problem with wireframe
representations is that CSG operations are not performed. If you use
CSG intersection, difference, or clipping, you may see a lot of stuff
that will not appear in the final image.
1.7.4 Depth Files
Instead of creating an image, it is possible to instruct Polyray to
create a file that contains depth information. Each pixel in the
resulting Targa will contain the distance from the eye to the first
surface that is hit. The format of the files is identical to the
formats used by height fields (see 2.3.2.9.1).
For example, to create a 24 bit depth file, the following command
could be used:
polyray sphere.pi -p 24 -d
Polyray will disable any antialiasing when creating depth files(it is
inappropriate to average depths).
There are three common applications for depth files: shadow maps (see
Depth Lights, 2.4.1.5), height fields, and creating Random Dot
Stereograms (RDS).
RDS images are an interesting way to create a 3D picture that doesn't
require special glasses. The program RDSGEN (available in the
16
GRAPHDEV forum on Compuserve) will accept Polyrays 24 bit depth format
and create an RDS image.
1.7.5 Raw Triangles
A somewhat odd addition to the image output formats for Polyray is the
generation of raw triangle information. What happens is very similar
to the scan conversion process, but rather than draw polygons, Polyray
will write a text description of the polygons (after splitting them
into triangles). The final output is a (usually long) list of lines,
each line describing a single smooth triangle. The format of the
output is one of the following:
x1 y1 z1 x2 y2 z2 x3 y3 z3
or
x1 y1 z1 x2 y2 z2 x3 y3 z3 nx1 ny1 nz1 nx2 ny2 nz2 nx3 ny3 nz3
u1 v1 u2 v2 u3 v3
If the output is raw triangles then only the three vertices are
printed. If uv_triangles are being created, then the normal
information for each of the vertices follows the vertices and the u/v
values follow them. The actual u/v values are specific to the type of
object being generated.
Currently I don't have any applications for this output, but the first
form is identical to the input format of the program RAW2POV. The
intent of this feature is to provide a way to build models in polygon
form for conversion to the input format of another renderer.
For example, to produce raw triangle output describing a sphere, and
dump it to a file you could use the command:
polyray sphere.pi -r 3 > sphere.tri
For full vertex, normal, and uv information, use the following
command:
polyray sphere.pi -r 4 > uvsphere.tri
Nothing is drawn on screen while the raw triangles are generated, so
typically you would turn off the graphics display (-V 0) when using
this option.
1.8 Display Options (IBM format only)
Polyray supports 15 distinct VESA display modes. The table below
lists the modes, the command line value to use with '-V x', and the
entry in the file polyray.ini to set a default video mode.
Resolution Colors Command Line Initialization name
---------------------------------------------------------------
N/A none -V 0 none
17
320x200 256 -V 1 vga/vga1
640x480 256 -V 2 vga2
800x600 256 -V 3 vga3
1024x768 256 -V 4 vga4
1280x1024 256 -V 5 vga5
320x200 32K -V 6 hicolor/hicolor1
640x480 32K -V 7 hicolor2
800x600 32K -V 8 hicolor3
1024x768 32K -V 9 hicolor4
1280x1024 32K -V 10 hicolor5
320x200 16M -V 11 truecolor1
640x480 16M -V 12 truecolor2
800x600 16M -V 13 truecolor3
1024x768 16M -V 14 truecolor4
1280x1024 16M -V 15 truecolor5
It is pretty unlikely you have a board that will do all of the
resolutions listed above. For that reason, Polyray will check to see
if the requested mode is possible on your board. If not, another mode
will be selected. The first priority is to find a mode with the same
# of bits per pixel (even if the screen resolution is lower). The
second priority is to drop to the next lower # of bits per pixel,
starting at the highest resolution possible.
Setting the default display mode in polyray.ini is done by inserting a
line of the form:
display hicolor1
To override a display mode from the command line you would do
something
like:
C> polyray sphere.pi -V 12
Note that using the line status display will mess up some of the
higher graphics modes. If this bothers you, use the flag -t 1 to
limit statistics to only startup and totals (or -t 0 for no
statistics).
2 Detailed description of the Polyray input format:
An input file describes the basic components of an image:
o A viewpoint that characterizes where the eye is, where it is
looking and what its orientation is.
o Objects, their shape, placement, and orientation.
o Light sources, their placement and color.
Beyond the fundamentals, there are many components that exist as a
18
convenience such as definable expressions and textures. This section
of the document describes in detail the syntax of all of the
components of an input file
2.1 Expressions
There are six basic types of expressions that are used in Polyray:
o float: Floating point expression (e.g., 0.5, 2 * sin(1.33)).
These are used at any point a floating point value is
needed, such as the radius of a sphere or the amount of
contribution of a part of the lighting model.
o vector: Vector valued expression (e.g., <0, 1, 0>, red,
12 * <2, sin(x), 17> + P). Used for color expressions,
describing positions, describing orientations, etc.
o arrays: Lists of expressions (e.g., [0, 1, 17, 42],
[<0,1,0>, <2*sin(theta), 42, -4>, 2*<3, 7, 2>])
o cexper: Conditional expression (e.g., x < 42).
o string: Strings used for file names or systems calls
o images: A Targa, GIF, or JPEG image.
The following sections describe the syntax for each of these types of
expressions, as well as how to define variables in terms of
expressions. See also the description of color maps, image maps (from
which you can retrieve color or vector values), indexed maps, and
height maps.
2.1.1 Numeric expressions
In most places where a number can be used (i.e. scale values, angles,
RGB components, etc.) a simple floating point expression (float) may
be used. These expressions can contain any of the following terms:
-0.1, 5e-3, ab, ... A floating point number or defined value
'(' float ')' Parenthesised expression
float ^ float Exponentiation, same as pow(x, y)
float * float Multiplication
float / float Division
float + float Addition
float - float Subtraction
-float Unary minus
acos(float) Arccosine, (radians for all trig functions)
asin(float) Arcsin
atan(float) Arctangent
atan2(float,float) Angle from x-axis to the point (x, y)
ceil(float) Ceiling function
cos(float) Cosine
cosh(float) Hyperbolic cosine
degrees(float) Converts radians to degrees
exp(float) e^x, standard exponential function
fabs(float) Absolute value
floor(float) Floor function
19
fmod(float,float) Modulus function for floating point values
heightmap(image,vector) Height of an pixel in an image
indexed(image,vector) Index of an pixel in an image
legendre(l,m,n) Legendre function
ln(float) Natural logarithm
log(float) Logarithm base 10
min(float,float) Minimum of the two arguments
max(float,float) Maximum of the two arguments
noise(vector)
noise(vector,float) Solid texturing (noise) function. If the
second argument is given, it is used as the
number of octaves (repetitions) of the 3D
noise function.
noise(vector,vector) Second arg provides more flexible operation
using: <pos scale, noise scale, octaves>
pow(float,float) Exponentiation (x^y)
radians(float) Converts degrees to radians
sawtooth(float) Sawtooth function (range is 0 - 1)
sin(float) Sine
sinh(float) Hyperbolic sine
sqrt(float) Square root
tan(float) Tangent
tanh(float) Hyperbolic tangent
visible(vector,vector) Returns 1 if second point visible from
first.
vector[i] Extract component i from a vector (0<=i<=3)
vector . vector Dot product of two vectors
|float| Absolute value (same as fabs)
|vector| Length of a vector
2.1.2 Vector Expressions
In most places where a vector can be used (i.e. color values, rotation
angles, locations, ...), a vector expression is allowed. The
expressions can contain any of the following terms:
vector + vector Addition
vector - vector Subtraction
vector * vector Cross product
vector * float Scaling of a vector by a scalar
float * vector Scaling of a vector by a scalar
vector / float Inverse scaling of a vector by a scalar
brownian(vector,vector) Makes a random displacement of the first
point by an amount proportional to the
components of the second point
brownian(vector) Random displacement of up to 0.1
color_wheel(x, y, z) RGB color wheel using x and z (y ignored),
the color returned is based on <x, z>
using the chart below:
Z-Axis
^
20
|
|
Green Yellow
\ /
\ /
Cyan ---- * ---- Red -----> X-Axis
/ \
/ \
Blue Magenta
Intermediate colors are generated by
interpolation.
dnoise(vector)
dnoise(vector,float) Returns a vector (gradient) based on the
location given in the first argument. If
the second argument is given, it is used as
the number of octaves (repetitions) of the
3D noise function
dnoise(vector,vector) Second arg provides more flexible operation
using: <pos scale, noise scale, octaves>
planar_imagemap(image, vector [, rflag])
cylindrical_imagemap(image, vector [, rflag])
spherical_imagemap(image, vector [, rflag])
environment_map(vector, environment)
Image map lookup functions. If the third
argument is given, then the image will be
tiled, otherwise black is used outside the
bounds of the image. Note: for planar
image maps only the x and z coordinates of
the second argument are used.
rotate(vector,vector) Rotate the point specified in the first
argument by the angles specified in the
second argument (angles in degrees).
rotate(vector,vector, Rotate the point specified in the first
float) argument about the axis specified in
the second argument by the angle given
in the third argument
reflect(vector,vector) Reflect the first vector about the second
vector (particularly useful in environment
maps)
trace(vector,vector) Color resulting from tracing a ray from the
the point given as the first argument in
the direction given by the second argument.
2.1.3 Arrays
Arrays are a way to represent data in a convenient list form. A good
use for arrays is to hold a number of locations for polygon vertices
or as locations for objects in successive frames of an animation.
As an example, a way to define a tetrahedron (4 sided solid) is to
define its vertices, and which vertices make up its faces. By using
21
this information in an object declaration, we can make a tetrahedron
out of polygons very easily.
define tetrahedron_faces
[<0, 1, 2>, <0, 2, 3>, <0, 3, 1>, <1, 3, 2>]
define tetrahedron_vertices
[<0, 0, sqrt(3)>,
<0, (2*sqrt(2)*sqrt(3))/3, -sqrt(3)/3>,
<-sqrt(2), -(sqrt(2)*sqrt(3))/3, -sqrt(3)/3>,
<sqrt(2), -(sqrt(2)*sqrt(3))/3, -sqrt(3)/3>]
define tcf tetrahedron_faces
define tcv tetrahedron_vertices
define tetrahedron
object {
object { polygon 3,tcv[tcf[0][0]],tcv[tcf[0][1]],tcv[tcf[0][2]]} +
object { polygon 3,tcv[tcf[1][0]],tcv[tcf[1][1]],tcv[tcf[1][2]]} +
object { polygon 3,tcv[tcf[2][0]],tcv[tcf[2][1]],tcv[tcf[2][2]]} +
object { polygon 3,tcv[tcf[3][0]],tcv[tcf[3][1]],tcv[tcf[3][2]]}
}
What happened in the object declaration is that each polygon grabbed a
series of vertex indices from the array tetrahedron_faces, then used
that index to grab the actual location in space of that vertex.
Another example is to use an array to store a series of view
directions so that we can use animation to generate a series of very
distinct renders of the same scene (the following example is how the
views for an environment map are generated):
define location <0, 0, 0>
define at_vecs [<1, 0, 0>, <-1, 0, 0>, < 0, 1, 0>, < 0,-1, 0>,
< 0, 0,-1>, < 0, 0, 1>]
define up_vecs [< 0, 1, 0>, < 0, 1, 0>, < 0, 0, 1>, < 0, 0,-1>,
< 0, 1, 0>, < 0, 1, 0>]
// Generate six frames
start_frame 0
end_frame 5
// Each frame generates the view in a specific direction. The
// vectors stored in the arrays at_vecs, and up_vecs turn the
// camera in such a way as to generate image maps correct for using
// in an environment map.
viewpoint {
from location
at location + at_vecs[frame]
up up_vecs[frame]
...
}
Sample files: plyhdrn.pi, environ.pi
22
2.1.4 Conditional Expressions
Conditional expressions are used in one of two places: conditional
processing of declarations (see section 2.7) or conditional value
functions.
cexper has one of the following forms:
!cexper
cexper && cexper
cexper || cexper
float < float
float <= float
float > float
float >= float
A use of conditional expressions is to define a texture based on other
expressions, the format of this expression is:
(cexper ? true_value : false_value)
Where true_value/false_value can be either floating point or vector
values. This type of expression is taken directly from the equivalent
in the C language. An example of how this is used (from the file
spot1.pi) is:
special surface {
color white
ambient (visible(W, throw_offset) == 0
? 0
: (P[0] < 1 ? 1
: (P[0] > throw_length ? 0
: (throw_length - P[0]) / throw_length)))
transmission (visible(W, throw_offset) == 1
? (P[0] < 1 ? 0
: (P[0] > throw_length ? 1
: P[0] / throw_length))
: 1), 1
}
In this case conditional statements are used to determine the surface
characteristics of a cone defining the boundary of a spotlight. The
amount of ambient light is modified with distance from the apex of the
cone, the visibility of the cone is modified based on both distance
and on a determination if the cone is behind an object with respect to
the source of the light.
2.1.5 Run-Time expressions
There are a few expressions that only have meaning during the
rendering process:
23
I Direction of the ray that struck the object
P Point of intersection in object coordinates
N Normal to the point of intersection in world
coordinates
W Point of intersection in world coordinates
U The u/v/w coordinate of the intersection point
x,y,z Components of the point in object coordinates
u,v,w Components of the uv-coordinate of the point
These expressions describe the interaction of a ray and an object. To
use them effectively, you need to understand the distinction between
world coordinates, object coordinates, and u/v coordinates. Object
coordinates describe a point or a direction with respect to an object
as it was originally defined. World coordinates describe the same
point after it has been rotated/scaled/translated. u/v coordinates
describe the point in a way natural to a particular object type (e.g.,
latitude and longitude for a sphere). Typically texturing is done in
either object coordinates or u/v coordinates so that as the object is
moved around the texture will move with it. On the other hand shading
is done in world coordinates.
The variables u, v, and w are specific to each surface type and in
general are the natural mapping for the surface (e.g., latitude and
longitude on a sphere) In general u varies from 0 to 1 as you go
around an object and v varies from 0 to one as you go from the bottom
to the top of an object. These variables can be used in a couple of
ways, to tell Polyray to only render portions of a surface within
certain uv bounds, or they can be used as arguments to expressions in
textures or displacement functions.
Not all primitives set meaningful values for u and v, those that do
are:
bezier, cone, cylinder, disc, height fields, NURB, parabola,
parametric, sphere, torus, patch
Other surface types will simply have u=x, v=y, w=z. An intersection
with a gridded object will use the u/v coordinates of the object
within the grid.
See the file uvtst.pi in the data archives for an example of using uv
bounds on objects. The file spikes.pi demonstrates using uv as
variables in a displacement surface. The file bezier1.pi demonstrates
using uv as variables to stretch an image over the surface of a bezier
patch.
The meanings of some of these variables are slightly different when
creating particle systems (see 2.3.5), or when coloring the background
of an image (see 2.4.2)
2.1.6 Named Expressions
24
A major convenience for creating input files is the ability to create
named definitions of surface models, object definitions, vectors, etc.
The way a value is defined takes one of the following forms:
define token expression float, vector, array, cexper
define token "str..." string expression
define token object { ... }
define token surface { ... }
define token texture { ... }
define token transform { ... } Each entry may be one of
scale/translate/rotate/shear
define token particle { ... }
Objects, surfaces, and textures can either be instantiated as-is by
using the token name alone, or it can be instantiated and modified:
token,
or
token { ... modifiers ... },
Polyray keeps track of what type of entity the token represents and
will parse the expressions accordingly.
Note: It is not possible to have two types of entities referred to by
the same name. If a name is reused, then a warning will be printed,
and all references to that name will use the new definition from that
point on.
2.1.6.1 Static variables
Static variables are a way to retain variable values (expressions,
objects, textures, ...) from frame to frame of an animation. Instead
of the normal declaration of a variable:
define xyz 32 * frame
you would do something like this:
if (frame == start_frame)
static define xyz 42
else
static define xyz (xyz + 0.1)
The big differences between a static define and a define are that the
static variable will be retained from frame to frame, and the static
variable actually replaces any previous definitions rather than simply
overloading them.
The static variables have an additional use beyond simple arithmetic
on variables. By defining something that takes a lot of processing at
parse time (like height fields and large image maps), you can make
them static in the first frame and simply instantiate them every frame
after that.
25
One example of this would be to spin a complex height field, if you
have to create it every frame, there is a long wait while Polyray
generates the field. The following declarations would be a better
way:
if (frame == start_frame)
static define sinsf
object {
smooth_height_fn 128, 128, -2, 2, -2, 2,
0.25 * sin(18.85 * x * z + theta_offset)
shiny_red
}
...
sinsf { rotate <0, 6*frame, 0> }
...
Several examples of how static variables can be used are found in the
animation directory in the data file archive. Two that make extensive
use of static variables are movsph.pi, which bounces several spherical
blob components around inside a box, and cannon.pi which points a
cannon in several directions, firing balls once it is pointed.
Warning: A texture inside a static object should ONLY be static
itself. The reason is that between frames, every non-static thing is
deallocated. If you have things inside a static object that point to
a deallocated pointer, you will most certainly crash the program.
Sorry, but detecting these things would be too hard and reallocating
all the memory would take up too much space.
2.1.6.2 Lazy Evaluation
Normally, Polyray tries to reduce the amount of time and space
necessary for evaluating and storing variables and expressions. For
example, if you had the following definition of x:
define x 2 * 3
Polyray would store 6 rather than the entire expression. This can
cause problems in certain circumstances if Polyray decides to reduce
the expression too soon. The problem is most notable in particle
systems since the expression that is used in it's declaration is used
for the life of the particle system. An example of the problem would
be a declaration like:
define t (frame - start_frame) / (end_frame - start_frame)
When Polyray encounters this it will store a value for t that is
between 0 (for the first frame) and 1 (for the last frame). This
great, up until you declare a particle system with something like:
if (frame == start_frame)
define partx
26
particle {
...
death (t > 0.5 ? 1 : 0)
}
Clearly the intent here is that the particles created by this system
will die halfway through the animation (t > 0.5). This won't happen,
since Polyray will evaluate the value of t at the time that partx is
declared, and store the expression (0 > 0.5 ? 1 : 0), which then
reduces to 0. This means the particle will never die.
To avoid this difficulty, the keyword noeval can be added to a
definition to force Polyray to postpone evaluation. The declaration
of t would now be:
define noeval t (frame - start_frame) / (end_frame - start_frame)
When partx is declared, the entire expression is now used and the
particles will die at the appropriate time. Note that noeval is
always the last keyword in a definition, and it works correctly with
static definitions as well as normal ones.
Sample file: part6.pi
2.2 Definition of the viewpoint
The viewpoint and its associated components define the position and
orientation the view will be generated from.
The format of the declaration is:
viewpoint {
from vector
at vector
up vector
angle float
hither float
resolution float, float
aspect float
yon float
max_trace_depth float
aperture float
max_samples float
focal_distance float
image_format float
pixel_encoding float
pixelsize float
antialias float
antialias_threshold float
}
All of the entries in the viewpoint declaration are optional and have
reasonable default values (see below). The order of the entries
27
defining the viewpoint is not important, unless you redefine some
field. (In which case the last is used.)
The parameters are:
aspect The ratio of width to height. (Default: 1.0.)
at The center of the image, in world
coordinates. (Default: <0, 0, 0>)
angle The field of view (in degrees), from the
center of the top row to the center of the
bottom row. (Default: 45)
from The location of the eye. (Default: <0, 0, -1>)
hither Distance to front of view pyramid. Any
intersection closer than this value will be
ignored. (Default: 1.0e-3)
resolution Number of columns and rows in the image.
(Default: 256x256)
up Which direction is up (Default: <0, 1, 0>)
yon Distance to back of view pyramid. Any
intersection beyond this distance will be
ignored. (Default: 1.0e5)
max_trace_depth This allows you to tailor the amount of
recursion allowed for scenes with reflection
and/or transparency. (Default: 5)
aperture If larger than 0, then extra rays are shot
(controlled by max_samples) to produce a
blurred image. Good values are between 0.1
and 0.5. (Default: 0)
max_samples Number of rays/pixel when performing focal
blur (Default: 4)
focal_distance Distance from the eye to the point that things
are in focus, this defaults to the distance
between from and at.
image_format If 0 then normal image, if 1 then depth image
pixel_encoding If 0 then uncompressed, if 1 then image is RLE
pixelsize Number of bits/pixel in image (Default: 16)
antialias Level of antialiasing to use (Default: 0)
antialias_threshold Threshold to start antialiasing
(Default: 0.01)
The view vectors will be coerced so that they are perpendicular to the
vector from the eye (from) to the point of interest (at).
A typical declaration is:
viewpoint {
from <0, 5, -5>
at <0, 0, 0>
up <0, 1, 0>
angle 30
resolution 320, 160
aspect 2
}
28
In this declaration the eye is five units behind the origin, five
units above the x-z plane and is looking at the origin. The up
direction is aligned with the y-axis, the field of view is 30 degrees
and the output file will default to 320x160 pixels.
In this example it is assumed that pixels are square, and hence the
aspect ratio is set to width/height. If you were generating an image
for a screen that has pixels half as wide as they are high then the
aspect ratio would be set to one.
Note that you can change from left handed coordinates (the default for
Polyray) to right handed by using a negative aspect ratio (e.g.,
aspect -4/3).
2.3 Objects/Surfaces
In order to make pictures, the light has to hit something. Polyray
supports several primitive objects. The following sections give the
syntax for describing the primitives, as well as how more complex
primitives can be built from simple ones.
An object declaration is how polyray associates a surface with its
lighting characteristics, and its orientation. This declaration
includes one of the primitive shapes (sphere, polygon, ...), and
optionally: a texture declaration (set to a matte white if none is
defined), orientation declarations, or a bounding box declaration.
The format the declaration is:
object {
shape_declaration
[texture_declaration]
[translate/rotate/scale declarations]
[subdivision declarations]
[displacement declaration]
[shading flag declaration]
[bounding box declaration]
[u/v bounds declaration]
}
The following sub-sections describe the format of the individual parts
of an object declaration. (Note: The shape declaration MUST be first
in the declaration, as any operations that follow have to have data to
work on.)
2.3.1 Object Modifiers
Object modifiers are statements within an object declaration that are
used to move and/or change the shape of the object.
The declarations are processed in the order they appear in the
declaration, so if you want to resize and object before moving it, you
need to put the scale statement before the translation statement. The
29
exception to this are displacement and u/v bounds. These act on the
underlying shape and are applied prior to any other object modifiers,
regardless of where they appear in the object declaration.
2.3.1.1 Position and Orientation Modifiers
The position, orientation, and size of an object can be modified
through one of four linear transformations: translation, rotation,
scaling, and shear. Other modifications that can be made to the hape
are displacement and u/v bounding.
2.3.1.1.1 Translation
Translation moves the position of an object by the number of units
specified in the associated vector. The format of the declaration is:
translate <xt, yt, zt>
2.3.1.1.2 Rotation
Rotation revolves the position of an object about the x, y, and z axes
(in that order). The amount of rotation is specified in degrees for
each of the axes. The direction of rotations follows a left-handed
convention: if the thumb of your left hand points along the positive
direction of an axis, then the direction your fingers curl is the
positive direction of rotation. (A negative aspect ratio in the
viewpoint flips everything around to a right handed system.)
The format of the declaration is:
rotate <xr, yr, zr>
For example the declaration:
rotate <30, 0, 20>
will rotate the object by 30 degrees about the x axis, followed by 20
degrees about the z axis.
Remember: Left Handed Rotations.
2.3.1.1.3 Scaling
Scaling alters the size of an object by a given amount with respect to
each of the coordinate axes. The format of the declaration is:
scale <xs, ys, zs>
Note that using 0 for any of the components of a scale is a bad idea.
It may result in a division by zero in Polyray, causing a program
crash. Use a small number like 1.0e-5 to flatten things. Usually you
30
will just want to use 1 for components that don't need scaling.
2.3.1.1.4 Shear
A less frequently used, but occasionally useful transformation is
linear shear. Shear scales along one axis by an amount that is
proportional to the location on another axis. The format of the
declaration is:
shear yx, zx, xy, zy, xz, yz
Typically only one or two of the components will be non-zero, for
example the declaration:
shear 0, 0, 1, 0, 0, 0
will shear an object more and more to the right as y gets larger and
larger. The order of the letters in the declaration is descriptive,
shear ... ab, ... means shear along direction a by the amount ab times
the position b.
This declaration should probably be split into three: one that
associates shear in x with the y and z values, one that associates
shear in y with x and z values, and one that associates shear in z
with x and y values.
You might want to look at the file xander.pi - this uses shear on
boxes to make diagonally slanted parts of letters.
2.3.1.1.5 Displace
The displacement operation causes a modification of the shape of an
object as it is being rendered. The amount and direction of the
displacement are specified by the statement:
displace vector
or
displace float
If a vector expression is given, then Polyray will displace each
vertex in the surface by the vector. If a floating point expression
is given, then Polyray will displace the surface along the direction
of the normal, by the amount given in the expression.
For effective results, the value of u_steps and v_steps should be set
fairly high. Polyray subdivides the surface, then performs the
displacement. If there are too few subdivisions of the surface then
the result will be a very coarse looking object.
An example of an object that has a displacement is shown below. The
displacement in the outer object is applied to each of the two spheres
31
within.
object {
object {
sphere <0,-1.5, 0>, 2
u_steps 32
v_steps 64
shiny_red
}
+ object {
sphere <0, 1.5, 0>, 2
u_steps 32
v_steps 64
shiny_blue
translate <0, -1.5, 0>
rotate <-20, 0, 30>
translate <0, 2.25, 0>
}
displace 0.5 * sin(5*y)
}
Sample Files: disp2.pi, disp3.pi, legen.pi, spikes.pi
2.3.1.1.6 UV Bounds
By adjusting the value of the u/v bounds, it is possible to only
render a selected portion of a surface. The format of the declaration
is:
uv_bounds low_u, high_u, low_v, high_v
For example, the following declaration will result in a wedge shaped
portion of a sphere:
object {
sphere <-2, 2, 0>, 1
uv_bounds 0.3, 1.0, 0.0, 0.8
}
The same effect could be achieved through the use of CSG with one or
more clipping planes. For most purposes (e.g., creating a hemisphere
by setting u_low to 0.5) the uv_bounds will easier to create and
faster to render.
Sample File: uvtst.pi
2.3.1.2 Bounding box
In order to speed up the process of determining if a ray intersects an
object and in order to define good bounds for surfaces such as
polynomials and implicit surfaces, a bounding box can be specified. A
short example of how it is used to define bounds of a polynomial:
32
define r0 3
define r1 1
define torus_expression (x^2 + y^2 + z^2 - (r0^2 + r1^2))^2 -
4 * r0^2 * (r1^2 - z^2)
object {
polynomial torus_expression
shiny_red
bounding_box <-(r0+r1), -(r0+r1), -r1>,
< (r0+r1), (r0+r1), r1>
}
The test for intersecting a ray against a box is much faster than
performing the test for the polynomial equation. In addition the box
helps the scan conversion process determine where to look for the
surface of the torus.
2.3.1.3 Subdivision of Primitives
The amount of subdivision of a primitive that is performed before it
is displayed as polygons is tunable. These declarations are used for
scan conversion of object, when creating displacement surfaces, and to
determine the quality of an implicit function. The declarations are:
u_steps n
v_steps m
w_steps l
uv_steps n, m
uv_steps n, m, l
Where u generally refers to the number of steps around the primitive
(the number of steps around the equator of a sphere for example). The
parameter v refers to the number of steps along the primitive
(latitudes on a sphere). Cone and cylinder primitives only require 1
step along v, but for smoothness may require many steps in u.
For blobs, polynomials, and implicit surfaces, the u_steps component
defines how many subdivisions along the x-axis, the v_steps component
defines how many subdivisions along the y-axis, and the w_steps
component defines how many subdivision along the z-axis.
2.3.1.4 Shading Flags
It is possible to tune the shading that will be performed for each
object. The values of each bit in the flag has the same meaning as
that given for global shading in section 1.7.1.4:
1 Shadow_Check Shadows will be generated on the object
2 Reflect_Check Reflectivity will be tested
4 Transmit_Check Check for refraction
8 Two_Sides If on, highlighting will be performed
on both sides of a surface.
33
16 UV_Check If on then u/v coordinates are calculated
32 Cast_Shadow If off then the object doesn't cast a shadow
By default, all objects have the following flags set: Shadow_Check,
Reflect_Chect, Transmit_Check, UV_Check, and Cast_Shadow. The two
sides check is normally off.
The declaration has the form:
shading_flags xx
For example, if the value 50 (32 + 16 + 2) is used for xx above, then
this object can be reflective and will cast shadows, however there
will be no tests for transparency, there will be no shading of the
back sides of surfaces, and there will be no shadows on the surface.
Note: the shading flag only affects the object in which the
declaration is made. This means that if you want the shading values
affected for all parts if a CSG object, then you will need a
declaration in every component.
2.3.2 Primitives
Primitives are the lowest level of shape description. Typically a
scene will contain many primitives, appearing either individually or
as aggregates using either Constructive Solid Geometry (CSG)
operations or gridded objects.
Descriptions of each of the primitive shapes supported by Polyray, as
well as references to data files that demonstrate them are given in
the following subsections. Following the description of the
primitives are descriptions of how CSG and grids can be built.
2.3.2.1 Bezier patches
A Bezier patch is a form of bicubic patch that interpolates its
control vertices. The patch is defined in terms of a 4x4 array of
control vertices, as well as several tuning values.
The format of the declaration is:
bezier subdivision_type, flatness_value,
u_subdivisions, v_subdivision,
[ 16 comma-separated vertices, i.e.
<x0, y0, z0>, <x1, y1, z1>, ..., <x15, y15, z15> ]
The subdivision_type and flatness_value are no longer used by Polyray.
They are retained in the declaration for backwards compatibility.
The number of levels of subdivision of the patch, in each direction,
is controlled by either the u_subdivisions and v_subdivisions given in
the bezier shape declaration or by the value of uv_steps (see section
34
2.3.1.3) if given later in the object declaration. The more
subdivisions allowed, the smoother the approximation to the patch,
however storage and processing time go up.
An example of a bezier patch is:
object {
bezier 2, 0.05, 3, 3,
<0, 0, 2>, <1, 0, 0>, <2, 0, 0>, <3, 0,-2>,
<0, 1, 0>, <1, 1, 0>, <2, 1, 0>, <3, 1, 0>,
<0, 2, 0>, <1, 2, 0>, <2, 2, 0>, <3, 2, 0>,
<0, 3, 2>, <1, 3, 0>, <2, 3, 0>, <3, 3,-2>
uv_steps 8, 8
rotate <30, -70, 0>
shiny_red
}
Sample files: bezier0.pi, teapot.pi, teapot.inc
2.3.2.2 Blob
A blob describes a smooth potential field around one or more
spherical, cylindrical, or planar components.
The format of the declaration is:
blob threshold:
blob_component1
[, blob_component2 ]
[, etc. for each component ]
The threshold is the minimum potential value that will be considered
when examining the interaction of the various components of the blob.
Each blob component one of two forms:
sphere <x, y, z>, strength, radius
cylinder <x0, y0, z0>, <x1, y1, z1>, strength, radius
plane <nx, ny, nz>, d, strength, dist
torus <x, y, z>, <dx, dy, dz>, major, strength, minor
The strength component describes how strong the potential field is
around the center of the component, the radius component describes the
maximum distance at which the component will interact with other
components. For a spherical blob component the vector <x,y,z> gives
the center of the potential field around the component. For a
cylindrical blob component the vector <x0, y0, z0> defines one end of
the axis of a cylinder, the vector <x1, y1, z1> defines the other end
of the axis of a cylinder. A planar blob component is defined by the
standard plane equation with <nx, ny, nz> defining the normal and 'd'
defining the distance of the plane from the origin along the normal.
Note: The ends of a cylindrical blob component are given hemispherical
caps.
35
Note: toroidal blob components won't render correctly in raytracing.
The numerical precision of a PC is insufficient. It will work
correctly in scan conversion or raw triangle output.
Note: The colon and the commas in the declaration really are
important.
An example of a blob is:
object {
blob 0.5:
cylinder <0, 0, 0>, <5, 0, 0>, 1, 0.7,
cylinder <1, -3, 0>, <3, 2, 0>, 1, 1.4,
sphere <3, -0.8, 0>, 1, 1,
sphere <4, 0.5, 0>, 1, 1,
sphere <1, 1, 0>, 1, 1,
sphere <1, 1.5, 0>, 1, 1,
sphere <1, 2.7, 0>, 1, 1
shiny_red
}
Note: since a blob is essentially a collection of 4th order
polynomials, it is possible to specify which quartic root solver to
use. See section 2.3.2.15, for a description of the root_solver
statement.
Sample files: blob.pi, tblob.pi
2.3.2.3 Box
A box is rectangular solid that has its edges aligned with the x, y,
and z axes. it is defined in terms of two diagonally opposite
corners. The alignment can be changed by rotations after the shape
declaration.
The format of the declaration is:
box <x0, y0, z0>, <x1, y1, z1>
Usually the convention is that the first point is the front-lower-left
point and the second is the back-upper-right point. The following
declaration is four boxes stacked on top of each other:
define pyramid
object {
object { box <-1, 3, -1>, <1, 4, 1> }
+ object { box <-2, 2, -2>, <2, 3, 2> }
+ object { box <-3, 1, -3>, <3, 2, 3> }
+ object { box <-4, 0, -4>, <4, 1, 4> }
matte_blue
36
}
Sample file: boxes.pi
2.3.2.4 Cone
A cone is defined in terms of a base point, an apex point, and the
radii at those two points. Note that cones are not closed (you must
use discs to cap them).
The format of the declaration is:
cone <x0, y0, z0>, r0, <x1, y1, z1>, r1
An example declaration of a cone is:
object {
cone <0, 0, 0>, 4, <4, 0, 0>, 0
shiny_red
}
Sample file: cone.pi
2.3.2.5 Cylinder
A cylinder is defined in terms of a bottom point, a top point, and its
radius. Note that cylinders are not closed.
The format of the declaration is:
cylinder <x0, y0, z0>, <x1, y1, z1>, r
An example of a cylinder is:
object {
cylinder <-3, -2, 0>, <0, 1, 3>, 0.5
shiny_red
}
Sample file: cylinder.pi
2.3.2.6 Disc
A disc is defined in terms of a center a normal and either a radius,
or using an inner radius and an outer radius. If only one radius is
given, then the disc has the appearance of a (very) flat coin. If two
radii are given, then the disc takes the shape of an annulus (washer)
where the disc extends from the first radius to the second radius.
Typical uses for discs are as caps for cones and cylinders, or as
ground planes (using a really big radius).
The format of the declaration is:
37
disc <cx, cy, cz>, <nx, ny, nz>, r
or
disc <cx, cy, cz>, <nx, ny, nz>, ir, or
The center vector <cx,cy,cz> defines where the center of the disc is
located, the normal vector <nx,ny,nz> defines the direction that is
perpendicular to the disc. i.e. a disc having the center <0,0,0> and
the normal <0,1,0> would put the disc in the x-z plane with the y-axis
coming straight out of the center.
An example of a disc is:
object {
disc <0, 2, 0>, <0, 1, 0>, 3
rotate <-30, 20, 0>
shiny_red
}
Note: a disc is infinitely thin. If you look at it edge-on it will
disappear.
Sample file: disc.pi
2.3.2.7 Glyphs (TrueType fonts)
The glyph primitive creates shapes similar to the sweep primitive.
The big difference is that straight line and curved line segments may
appear in the same contour. Additionally, the glyph can be made from
several contours with exterior ones defining the outsides of the shape
and interior ones defining holes within the glyph.
Typically the glyph primitive will be used in conjunction with the
utility TTFX (described below) to translate TrueType font information
into Polyray glyph information.
The following declaration creates a box with a square hole cut out of
it.
object {
glyph 2
contour 4,
<0, 0>, <4, 0>, <4, 4>, <0, 4>
contour 4,
<1, 1>, <1, 3>, <3, 3>, <3, 1>
texture { shiny { color red reflection 0.2 } }
translate <-2, 0, 0>
}
The default placement of glyph coordinates is in the x-y plane and the
glyph has a depth of one in z (starting at z=0 and going to z=1). To
change the depth, just use something like: scale <1, 1, 0.2>.
38
Each entry in a contour is a 2D point. If a non-zero z component is
added then the point is assumed to be an off-curve point and will
create a curved segment within the contour. For example, the
following declaration makes a somewhat star shaped contour with sharp
corners for the inner parts of the star and rounded curves for the
outer parts of the star:
object {
glyph 1 contour 14,
<r0*cos( 1*dt),r0*sin( 1*dt)>,<r1*cos( 2*dt),r1*sin( 2*dt),1>,
<r0*cos( 3*dt),r0*sin( 3*dt)>,<r1*cos( 4*dt),r1*sin( 4*dt),1>,
<r0*cos( 5*dt),r0*sin( 5*dt)>,<r1*cos( 6*dt),r1*sin( 6*dt),1>,
<r0*cos( 7*dt),r0*sin( 7*dt)>,<r1*cos( 8*dt),r1*sin( 8*dt),1>,
<r0*cos( 9*dt),r0*sin( 9*dt)>,<r1*cos(10*dt),r1*sin(10*dt),1>,
<r0*cos(11*dt),r0*sin(11*dt)>,<r1*cos(12*dt),r1*sin(12*dt),1>,
<r0*cos(13*dt),r0*sin(13*dt)>,<r1*cos(14*dt),r1*sin(14*dt),1>
}
The program TTFX.EXE has been included to help with the conversion of
TrueType fonts from their .TTF format (typically found in the
/WINDOWS/SYSTEM directory) into the sort of declaration that Polyray
can understand. For example,
ttfx \windows\system\times.ttf Foo > temp.pi
or,
ttfx \windows\system\times.ttf Foo 0.2 > temp.pi
By default, the characters in the string being converted (the word Foo
above) are packed right next to each other. If a number follows the
text string, then the characters are separated by that amount. A
spacing of 0.2 has worked pretty well for the fonts I've tried.
If you then add the following line to the top of temp.pi (leaving
everything else like viewpoint to their defaults)
light <0, 100, -100>
and render it with
polyray temp.pi -r 1
you will get a nice rendered image of the word Foo.
Note that the combination of TTFX, Polyray raw triangle output, and
RAW2POV is a way you can create TrueType characters for use in a
number of renderers.
Sample files: timestst.pi, glyph1.pi, glyph2.pi, glyph3.pi, wingtst.pi
2.3.2.8 Implicit Surface
The format of the declaration is:
39
function f(x,y,z)
The function f(x,y,z) may be any expression composed of the variables:
x, y, z, a numerical value (e.g., 0.5), the operators: +, -, *, /, ^,
and any Polyray supported function. The code is not particularly
fast, nor is it totally accurate, however the capability to ray-trace
such a wide class of functions by a SW program is (I believe) unique
to Polyray.
The following object is taken from sombrero.pi and is a surface that
looks very much like diminishing ripples on the surface of water.
define a_const 1.0
define b_const 2.0
define c_const 3.0
define two_pi_a 2.0 * 3.14159265358 * a_const
// Define a diminishing cosine surface (sombrero)
object {
function y - c_const * cos(two_pi_a * sqrt(x^2 + z^2)) *
exp(-b_const * sqrt(x^2 + z^2))
matte_red
bounding_box <-4, -4, -4>, <4, 4, 4>
}
Rendering the following object will show all the places within a
sphere where the solid noise function takes on the value 0.5.
object {
object {
function noise(3*P) - 0.5
u_steps 64
v_steps 64
w_steps 64
Lapis_Lazuli
bounding_box <-2, -2, -2>, <2, 2, 2>
}
& object { sphere <0, 0, 0>, 2 }
rotate <-10, 20, 0>
}
It is quite important to have the bounding box declaration within the
object where the function is declared. If that isn't there, Polyray
will be unable to determine where to look for intersections with the
surface. (The bounding box of an implicit function defaults to <-1, -
1, -1>, <1, 1, 1>.)
Sample files: noisefn.pi, sinsurf.pi, sectorl.pi, sombrero.pi,
superq.pi, zonal.pi
2.3.2.9 Height Field
40
There are two ways that height fields can be specified, either by
using data stored in a Targa file, or using an implicit function of
the form y = f(x, z).
The default orientation of a height field is that the entire field
lies in the square 0 <= x <= 1, 0 <= z <= 1. File based height fields
are always in this orientation, implicit height fields can optionally
be defined over a different area of the x-z plane. The height value
is used for y.
2.3.2.9.1 File Based Height Fields
Height fields data can be read from any Targa, GIF, or JPEG format
file. A GIF image will be treated as an 8 bit format image as
described below. Any color information in the GIF file is ignored.
Note that if you use JPEG images, a grayscale JPEG will be treated as
an 8 bit format and a color JPEG will be treated as a 24 bit format
height field. Due to the lossy nature of JPEG, it is extremely
unlikely that a color JPEG will be useful as a height field. It is
possible that reasonable results can be obtained using grayscale JPEG
images as height fields (no guarantees).
By using smooth_ in front of the declaration, an extra step is
performed that calculates normals to the height field at every point
within the field. The result of this is a greatly smoothed
appearance, at the expense of around three times as much memory being
used.
The format of the declaration is:
height_field "filename"
smooth_height_field "filename"
The sample program wake.c generates a Targa file that simulates the
wake behind a boat.
Sample files: wake.pi, spiral.pi, islands.pi
2.3.2.9.1.1 8 Bit Format
Each pixel in the file is represented by a single byte. The value of
the byte is used as an integer height between -127 and 128.
2.3.2.9.1.2 16 Bit Format
Each pixel in the file is represented by two bytes, low then high.
The high component defines the integer component of the height, the
low component holds the fractional part scaled by 255. The entire
value is offset by 128 to compensate for the unsigned nature of the
storage bytes. As an example the values high = 140, low = 37 would
41
be translated to the height:
(140 + 37 / 256) - 128 = 12.144
similarly if you are generating a Targa file to use in Polyray, given
a height, add 128 to the height, extract the integer component, then
extract the scaled fractional component. The following code fragment
shows a way of generating the low and high bytes from a floating point
number.
unsigned char low, high;
float height;
FILE *height_file;
...
height += 128.0;
high = (unsigned char)height;
height -= (float)high;
low = (unsigned char)(256.0 * height);
fputc(low, height_file);
fputc(high, height_file);
2.3.2.9.1.3 24 Bit Format
The red component defines the integer component of the height, the
green component holds the fractional part scaled by 255, the blue
component is ignored. The entire value is offset by 128 to compensate
for the unsigned nature of the RGB values. As an example the values
r = 140, g = 37, and b = 0 would be translated to the height:
(140 + 37 / 256) - 128 = 12.144
similarly if you are generating a Targa file to use in Polyray, given
a height, add 128 to the height, extract the integer component, then
extract the scaled fractional component. The following code fragment
shows
a way of generating the RGB components from a floating point number.
unsigned char r, g, b;
float height;
FILE *height_file;
...
height += 128.0;
r = (unsigned char)height;
height -= (float)r;
g = (unsigned char)(256.0 * height);
b = 0;
fputc(b, height_file);
fputc(g, height_file);
fputc(r, height_file);
42
2.3.2.9.1.4 32 Bit Format
The four bytes of the 32 bit Targa image are used to hold the four
bytes of a floating point number. The format of the floating point
number is machine specific, and the order of the four bytes in the
image correspond exactly to the order of the four bytes of the
floating number when it is in memory.
For example, the following code shows how to take a floating point
number and store it to a file in the format that Polyray will expect.
You will also need to write the Targa header, etc.
unsigned char *byteptr;
float depth;
FILE *ofile;
... calculations for depth ...
/* Store a floating point number as a 32 bit color- this is
obviously a machine specific result for the floating point
number that is stored. There is also an assumption here
that a float type is exactly 4 bytes and that the
size of an unsigned char is exactly 1 byte. */
byteptr = (unsigned char *)&depth;
fputc(byteptr[0], ofile);
fputc(byteptr[1], ofile);
fputc(byteptr[2], ofile);
fputc(byteptr[3], ofile);
...
2.3.2.9.2 Implicit Height Fields
Another way to define height fields is by evaluating a mathematical
function over a grid. Given a function y = f(x, z), Polyray will
evaluate the function over a specified area and generate a height
field based on the function. This method can be used to generate
images of many sorts of functions that are not easily represented by
collections of simpler primitives.
The valid formats of the declaration are:
height_fn xsize, zsize, minx, maxx, minz, maxz, expression
height_fn xsize, zsize, expression
smooth_height_fn xsize, zsize, minx, maxx, minz, maxz, expression
smooth_height_fn xsize, zsize, expression
If the four values minx, maxx, minz, and maxz are not defined then the
default square 0 <= x <= 1, 0 <= z <= 1 will be used.
For example,
43
// Define constants for the sombrero function
define a_const 1.0
define b_const 2.0
define c_const 3.0
define two_pi_a 2.0 * 3.14159265358 * a_const
// Define a diminishing cosine surface (sombrero)
object {
height_fn 80, 80, -4, 4, -4, 4,
c_const * cos(two_pi_a * sqrt(x^2 + z^2)) *
exp(-b_const * sqrt(x^2 + z^2))
shiny_red
}
will build a height field 80x80, covering the area from -4 <= x <= 4,
and -4 <= z <= 4.
Compare the run-time performance and visual quality of the sombrero
function as defined in sombfn.pi with the sombrero function as defined
in sombrero.pi. The former uses a height field representation and
renders quite fast. The latter uses a very general function
representation and gives smoother but very slow results.
Sample file: sombfn.pi, sinfn.pi
2.3.2.10 Lathe surfaces
A lathe surface is a polygon that has been revolved about the y-axis.
This surface allows you to build objects that are symmetric about an
axis, simply by defining 2D points.
The format of the declaration is:
lathe type, direction, total_vertices,
<vert1.x,vert1.y,vert1.z>
[, <vert2.x, vert2.y, vert2.z>]
[, etc. for total_vertices vertices]
The value of type is either 1, or 2. If the value is 1, then the
surface will simply revolve the line segments. If the value is 2,
then the surface will be represented by a spline that approximates the
line segments that were given. A lathe surface of type 2 is a very
good way to smooth off corners in a set of line segments.
The value of the vector direction is used to change the orientation of
the lathe. For a lathe surface that goes straight up and down the y-
axis, use <0, 1, 0> for direction. For a lathe surface that lies on
the x-axis, you would use <1, 0, 0> for the direction.
Sample file: lathe1.pi, lathe2.pi, lathe3.pi
Note that CSG will really only work correctly if you close the lathe -
44
that is either make the end point of the lathe the same as the start
point, or make the x-value of the start and end points equal zero.
Lathes, unlike polygons are not automatically closed by Polyray.
Note: since a splined lathe surface (type = 2) is a 4th order
polynomial, it is possible to specify which quartic root solver to
use. See section 2.3.2.15 for a description of the root_solver
statement.
2.3.2.11 NURBS
Polyray supports the general patch type, Non-Uniform Rational B-
Splines (NURBS). All that is described here is how they are declared
and used in Polyray. For further background and details on NURBS,
refer to the literature.
They are declared with the following syntax:
nurb u_order, u_points, v_order, v_points,
u_knots, v_knots, uv_mesh
or
nurb u_order, u_points, v_order, v_points, uv_mesh
Where each part of the declaration has the following format and
definition,
Name Format Definition
u_order integer One more than the power of the spline in
the u direction. (If u_order = 4, then it
will be a cubic patch.)
u_points integer The number of vertices in each row of the
patch mesh
v_order integer One more than the power of the spline
in v direction.
v_points integer The number of rows in the patch mesh
u_knots [...] Knot values in the u direction
v_knots [...] Knot values in the v direction
uv_mesh [[...] An array of arrays of vertices. Each
vertex may have either three or four
[...]] components. If the fourth component is
set then the spline will be rational.
If the vertex has only three components
then the homogenous (fourth) component is
assumed to be one. The homogenous
component must be greater than 0.
For example, the following is a complete declaration of a NURB patch
object {
nurb 4, 6, 4, 5,
[0, 0, 0, 0, 1.5, 1.5, 3, 3, 3, 3], // Non-uniform knots
[0, 0, 0, 0, 1, 2, 2, 2, 2], // Uniform open knots
[[<0,0,0>, <1,0, 3>, <2,0,-3>, <3,0, 3>, <4,0,0>],
45
[<0,1,0>, <1,1, 0>, <2,1, 0>, <3,1, 0>, <4,1,0>],
[<0,2,0>, <1,2, 0>, <2,2, 5,2>, <3,2, 0>, <4,2,0>],
[<0,3,0>, <1,3, 0>, <2,3, 5,0.5>, <3,3, 0>, <4,3,0>],
[<0,4,0>, <1,4, 0>, <2,4, 0>, <3,4, 0>, <4,4,0>],
[<0,5,0>, <1,5,-3>, <2,5, 3>, <3,5,-3>, <4,5,0>]]
translate <-2, -2.5, 0>
rotate <-90, -30, 0>
uv_steps 32, 32
shiny_red
}
The preceeding patch was both non-uniform and rational. If you don't
want to bother declaring the knot vector you can simply omit it. This
will result in a open uniform B-Spline patch. Most of the time the
non-uniform knot vectors are unnecessary and can be safely omitted.
The preceeding declaration with uniform knot vectors, and non-rational
vertices could then be declared as:
object {
nurb 4, 6, 4, 5,
[[< 0, 0, 0>, < 1, 0, 3>, < 2, 0,-3>, < 3, 0, 3>, < 4, 0, 0>],
[< 0, 1, 0>, < 1, 1, 0>, < 2, 1, 0>, < 3, 1, 0>, < 4, 1, 0>],
[< 0, 2, 0>, < 1, 2, 0>, < 2, 2, 5>, < 3, 2, 0>, < 4, 2, 0>],
[< 0, 3, 0>, < 1, 3, 0>, < 2, 3, 5>, < 3, 3, 0>, < 4, 3, 0>],
[< 0, 4, 0>, < 1, 4, 0>, < 2, 4, 0>, < 3, 4, 0>, < 4, 4, 0>],
[< 0, 5, 0>, < 1, 5,-3>, < 2, 5, 3>, < 3, 5,-3>, < 4, 5, 0>]]
translate <-2, -2.5, 0>
rotate <-90, -30, 0>
uv_steps 32, 32
shiny_red
}
Note that internally NURBS are stored as triangles. This can result
in a high memory usage for a finely diced NURB (uv_steps large).
Sample files: nurb0.pi, nurb1.pi
2.3.2.12 Parabola
A parabola is defined in terms of a bottom point, a top point, and its
radius at the top.
The format of the declaration is:
parabola <x0, y0, z0>, <x1, y1, z1>, r
The vector <x0,y0,z0> defines the top of the parabola - the part that
comes to a point. The vector <x1,y1,z1> defines the bottom of the
parabola, the width of the parabola at this point is r.
An example of a parabola declaration is:
object {
46
parabola <0, 6, 0>, <0, 0, 0>, 3
translate <16, 0, 16>
steel_blue
}
This is sort of like a salt shaker shape with a rounded top and the
base on the x-z plane.
2.3.2.13 Parametric surface
A parametric surface allows the creation of surfaces as a mesh of
triangles. By defining the vertices of the mesh in terms of functions
of u and v, Polyray will automatically create the entire surface. The
smoothness of the surface is determined by the number of steps allowed
for u and v.
The mesh defaults to 0 <= u <= 1, and 0 <= v <= 1. By explicitly
defining the uv_bounds for the surface it is possible to create only
the desired parts of the surface.
The format of the declaration is:
parametric <fx(u,v), fy(u,v), fz(u,v)>
For example, the following declarations could be used to create a
torus:
define r0 1.25
define r1 0.5
define torux (r0 + r1 * cos(v)) * cos(u)
define toruy (r0 + r1 * cos(v)) * sin(u)
define toruz r1 * sin(v)
object {
parametric <torux,toruy,toruz>
rotate <-20, 0, 0>
shiny_red
uv_bounds 0, 2*pi, 0, 2*pi
uv_steps 16, 8
}
2.3.2.14 Polygon
Although polygons are not very interesting mathematically, there are
many sorts of objects that are much easier to represent with polygons.
Polyray assumes that all polygons are closed and automatically adds a
side from the last vertex to the first vertex.
The format of the declaration is:
polygon total_vertices,
47
<vert1.x,vert1.y,vert1.z>
[, <vert2.x, vert2.y, vert2.z>]
[, etc. for total_vertices vertices]
As with the sphere, note the comma separating each vertex of the
polygon.
I use polygons as a floor in a lot of images. They are a little
slower than the corresponding plane, but for scan conversion they are
a lot easier to handle. An example of a checkered floor made from a
polygon is:
object {
polygon 4, <-20,0,-20>, <-20,0,20>, <20,0,20>, <20,0,-20>
texture {
checker matte_white, matte_black
translate <0, -0.1, 0>
scale <2, 1, 2>
}
}
Sample file: polygon.pi
2.3.2.15 Polynomial surface
The format of the declaration is:
polynomial f(x,y,z)
The function f(x,y,z) must be a simple polynomial, i.e. x^2+y^2+z^2-
1.0 is the definition of a sphere of radius 1 centered at (0,0,0).
For quartic (4th order) equations, there are three ways that Polyray
can use to solve for roots. By specifying which one is desired, it is
possible to tune for quality or speed. The method of Ferrari is the
fastest, but also the most numerically unstable. By default the
method of Vieta is used. Sturm sequences (which are the slowest)
should be used where the highest quality is desired.
The declaration of which root solver to use takes one of the forms:
root_solver Ferrari
root_solver Vieta
root_solver Sturm
(Capitalization is important - these are proper nouns after all.)
Note: due to unavoidable numerical inaccuracies, not all polynomial
surfaces will render correctly from all directions.
The following example, taken from devil.pi defines a quartic
polynomial. The use of the CSG clipping object is to trim
uninteresting parts of the surface. The bounding box declaration
48
helps the scan conversion routines figure out where to look for the
surface.
// Variant of a devil's curve in 3-space. This figure has a top
// and bottom part that are very similar to a hyperboloid of one
// sheet, however the central region is pinched in the middle
// leaving two teardrop shaped holes.
object {
object { polynomial x^4 + 2*x^2*z^2 - 0.36*x^2 - y^4 +
0.25*y^2 + z^4
root_solver Ferrari }
& object { box <-2, -2, -0.5>, <2, 2, 0.5> }
bounding_box <-2, -2, -0.5>, <2, 2, 0.5>
rotate <10, 20, 0>
translate <0, 3, -10>
shiny_red
}
Note: as the order of the polynomial goes up, the numerical accuracy
required to render the surface correctly also goes up. One problem
that starts to rear its ugly head starting at around 3rd to 4th order
equations is a problem with determining shadows correctly. The result
is black spots on the surface. You can ease this problem to a certain
extent by making the value of shadow_tolerance larger. For 4th and
higher equations, you will want to use a value of at least 0.05,
rather than the default 0.001.
Sample file: torus.pi, many others
2.3.2.16 Spheres
Spheres are the simplest 3D object to render and a sphere primitive
enjoys a speed advantage over most other primitives.
The format of the declaration is:
sphere <center.x, center.y, center.z>, radius
Note the comma after the center vector, it really is necessary.
My basic benchmark file is a single sphere, illuminated by a single
light. The definition of the sphere is:
object {
sphere <0, 0, 0>, 2
shiny_red
}
Sample file: sphere.pi
2.3.2.17 Sweep surface
A sweep surface, also referred to as an extruded surface, is a polygon
that has been swept along a given direction. It can be used to make
49
multi-sided beams, or to create ribbon-like objects.
The format of the declaration is:
sweep type, direction, total_vertices,
<vert1.x,vert1.y,vert1.z>
[, <vert2.x, vert2.y, vert2.z>]
[, etc. for total_vertices vertices]
The value of type is either 1, or 2. If the value is 1, then the
surface will be a set of connected squares. If the value is 2, then
the surface will be represented by a spline that approximates the line
segments that were given.
The value of the vector direction is used to change the orientation of
the sweep. For a sweep surface that is extruded straight up and down
the y-axis, use <0, 1, 0> for direction. The size of the vector
direction will also affect the amount of extrusion (e.g., if
|direction| = 2, then the extrusion will be two units in that
direction).
An example of a sweep surface is:
// Sweep made from connected quadratic splines.
object {
sweep 2, <0, 2, 0>, 16,
<0, 0>, <0, 1>, <-1, 1>, <-1, -1>, <2, -1>, <2, 3>,
<-4, 3>, <-4, -4>, <4, -4>, <4, -11>, <-2, -11>,
<-2, -7>, <2, -7>, <2, -9>, <0, -9>, <0, -8>
translate <0, 0, -4>
scale <1, 0.5, 1>
rotate <0,-45, 0>
translate <10, 0, -18>
shiny_yellow
}
Sample file: sweep1.pi, sweep2.pi
Note: CSG will really only work correctly if you close the sweep -
that is make the end point of the sweep the same as the start point.
Sweeps, unlike polygons are not automatically closed by Polyray.
See the description of glyphs for a more general swept surface.
2.3.2.18 Torus
The torus primitive is a doughnut shaped surface that is defined by a
center point, the distance from the center point to the middle of the
ring of the doughnut, the radius of the ring of the doughnut, and the
orientation of the surface.
The format of the declaration is:
50
torus r0, r1, <center.x, center.y, center.z>,
<dir.x, dir.y, dir.z>
As an example, a torus that has major radius 1, minor radius 0.4, and
is oriented so that the ring lies in the x-z plane would be declared
as:
object {
torus 1, 0.4, <0, 0, 0>, <0, 1, 0>
shiny_red
}
Note: since a torus is a 4th order polynomial, it is possible to
specify which quartic root solver to use. See section 2.3.2.15 for a
description of the root_solver statement.
Sample file: torus.pi
2.3.2.19 Triangular patches
A triangular patch is defined by a set of vertices and their normals.
When calculating shading information for a triangular patch, the
normal information is used to interpolate the final normal from the
intersection point to produce a smooth shaded triangle.
The format of the declaration is:
patch <x0,y0,z0>, <nx0,ny0,nz0>, [UV u0, v0,]
<x1,y1,z1>, <nx1,ny1,nz1>, [UV u1, v1,]
<x2,y2,z2>, <nx2,ny2,nz2> [, UV u2, v2]
The vertices and normals are required for each of the three corners of
the patch. The u/v coordinates are optional. If they are omitted,
then they will be set to the following values:
u0 = 0, v0 = 0
u1 = 1, v1 = 0
u2 = 0, v1 = 1
Smooth patch data is usually generated as the output of another
program.
2.3.3 Constructive Solid Geometry (CSG)
Objects can be defined in terms of the union, intersection, and
inverse of other objects. The operations and the symbols used are:
csgexper + csgexper - Union
csgexper * csgexper - Intersection
csgexper - csgexper - Difference
csgexper & csgexper - Clip the first object by the second
~csgexper - Inverse
51
(csgexper) - Parenthesised expression
Note that intersection and difference require a clear inside and
outside. Not all primitives have well defined sides. Those that do
are:
Spheres, Boxes, Glyphs, Polynomials, Blobs, Tori, and Functions.
Other surfaces that do not always have a clear inside/outside, but
work reasonably well in CSG intersections are:
Cylinders, Cones, Discs, Height Fields, Lathes, Parabola, Polygons,
and Sweeps.
Using Cylinders, Cones, and Parabolas works correctly, but the open
ends of these surfaces will also be open in the resulting CSG. To
close them off you can use a disc shape.
Using Discs, and Polygons in a CSG is really the same as doing a CSG
with the plane that they lie in. If fact, a large disc is an
excellent choice for clipping or intersecting an object, as the
inside/outside test is very fast.
Lathes, and Sweeps use Jordan's rule to determine if a point is
inside. This means that given a test point, if a line from that point
to infinity crosses the surface an odd number of times, then the point
is inside. The net result is that if the lathe (or sweep) is not
closed, then you may get odd results in a CSG intersection (or
difference).
CSG involving height fields only works within the bounds of the field.
As an example, the following object is a sphere of radius 1 with a
hole of radius 0.5 through the middle:
define cylinder_z object { cylinder <0,0,-1.1>, <0,0,1.1>, 0.5 }
define unit_sphere object { sphere <0, 0, 0>, 1 }
// Define a CSG shape by deleting a cylinder from a sphere
object {
unit_sphere - cylinder_z
shiny_red
}
Sample files: csg1.pi, csg2.pi, csg3.pi, roman.pi, lens.pi,
polytope.pi
2.3.4 Gridded objects
A gridded object is a way to compactly represent a rectangular
arrangement of objects by using an image map. Each object is placed
within a 1x1 cube that has its lower left corner at the location <i,
0, j> and its upper right corner at <i+1, 1, j+1>. The color index of
52
each pixel in the image map is used to determine which of a set of
objects will be placed at the corresponding position in space.
The gridded object is much faster to render than the corresponding
layout of objects. The major drawback is that every object must be
scaled and translated to completely fit into a 1x1x1 cube that has
corners at <0,0,0> and <1,1,1>.
The size of the entire grid is determined by the number of pixels in
the image. A 16x32 image would go from 0 to 16 along the x-axis and
the last row would range from 0 to 16 at 31 units in z out from the x-
axis.
The format of the declaration is:
gridded "image.tga",
object1
object2
object3
...
An example of how a gridded object is declared is:
define tiny_sphere object { sphere <0.5, 0.4, 0.5>, 0.4 }
define pointy_cone object { cone <0.5, 0.5, 0.5>, 0.4,
<0.5, 1, 0.5>, 0 }
object {
gridded "grdimg0.tga",
tiny_sphere { shiny_coral }
tiny_sphere { shiny_red }
pointy_cone { shiny_green }
translate <-10, 0, -10>
rotate <0, 210, 0>
}
In the image grdimg0.tga, there are a total of 3 colors used, every
pixel that uses color index 0 will generate a shiny coral colored
sphere, every pixel that uses index will generate a red sphere, every
pixel that uses index 2 will generate a green cone, and every other
color index used in the image will leave the corresponding space
empty.
The normal image format for a gridded object is either grayscale or
color mapped. To determine which object will be used, the value of
the pixel itself in the grayscale image and the color index is used in
the mapped image. If a 16 bit Targa is used, then the second byte of
the color is used. If a 24 bit Targa is used, then the value of the
red component is used. This limits the # of objects for all images to
256.
A color JPEG is treated the same as a 24 bit Targa (unlikely to be
useful, due to the lossy nature of JPEG).
53
Sample files: grid0.pi, river.pi
2.3.5 Particle Systems
There are two distinct pieces of information that are used by Polyray
to do particles. The first is a particle declaration, this is the
particle generator. The second is the particle itself. It is
important to retain the distinction, you generally only want to have
one particle generator, but you may want that generator to produce
many particles.
The form of the declaration for a particle generator is:
particle {
object "name"
position vector
velocity vector
acceleration vector
birth float
death float
count float
avoid float
}
The wrapper for the particle must be the name of an object appearing
in a define statement. If there isn't an object with that name,
Polyray will write an error message and abort. Everything else is
optional, but if you don't set either the velocity or acceleration
then the object will just sit at the origin.
The default values for each component are:
position - <0, 0, 0>
velocity - <0, 0, 0>
acceleration - <0, 0, 0>
birth - frame == start_frame
death - false (never dies)
count - 1
avoid - false (doesn't bounce)
As an example, the following declaration makes a starburst of 50
spheres. Note the conditional before the particle declaration. You
almost always want to do this, otherwise you will create a new
particle generator at every frame of the animation. Since the default
birth condition is to generate particles only on the first frame, the
only side effect here would be to suck up more memory every frame to
retain the particle generator definition.
frame_time 0.05
define gravity -1
// Star burst
54
if (frame == start_frame)
particle {
position <0, 5, 0>
velocity brownian(<0, 0, 0>, <1, 1, 1>)
acceleration gravity
object "bsphere"
count 50
}
The value in the velocity declaration generates a random vector that
ranges from <-1, -1, -1> to <1, 1, 1>. Each particle of the 50 is
given a different initial velocity, which is what gives the bursting
effect.
An additional declaration, frame_time xx, has been added specifically
for tuning particle animations. This declaration determines how much
time passes for every frame. Each particle starts with an age of 0,
after each frame it's age is incremented by the value xx in the
frame_time declaration. Additionally the position and velocity of the
particle is updated after every frame according to the formula:
V = V + frame_time * A
P = P + frame_time * V
The status of a particle is set by making use of some of the standard
Polyray variables. The names and meanings are:
P - Current location of the particle as a vector
x - X location of the particle (or P[0])
y - Y location of the particle (or P[1])
z - Z location of the particle (or P[2])
I - Current velocity of the particle as a vector
u - Age of the particle (frame_time * elapsed frames since birth)
These values are used in two situations, when checking the death
condition of a particle and when calculating the acceleration of the
particle.
If an avoid statement is given then before every frame the position of
the particle is checked to see if it hits any objects in the scene
(non-particle objects). If so, then the particle will bounce off the
object.
Note: see section 1.6.1.2 for information on lazy evaluation of
expressions. This is necessary for some particle systems to behave
correctly.
Sample files: part1.pi, part2.pi, part3.pi, part6.pi
2.4 Color and lighting
The color space used in polyray is RGB, with values of each component
specified as a value from 0 -> 1. The way the color and shading of
55
surfaces is specified is described in the following sections.
RGB colors are defined as either a three component vector, such as
<0.6, 0.196078, 0.8>, or as one of the X11R3 named colors (which for
the value given is DarkOrchid). One of these days when I feel like
typing and not thinking (or if I find them on line), I'll put in the
X11R4 colors.
The coloring of objects is determined by the interaction of lights,
the shape of the surface it is striking, and the characteristics of
the surface itself.
2.4.1 Light sources
Light sources are one of: simple positional light sources, spot
lights, or textured lights. None of these lights have any physical
size. The lights do not appear in the scene, only the effects of the
lights.
2.4.1.1 Positional Lights
A positional light is defined by its RGB color and its XYZ position.
The formats of the declaration are:
light color, location
light location
The second declaration will use white as the color.
2.4.1.2 Spot Lights
The formats of the declaration are:
spot_light color, location, pointed_at, Tightness, Angle, Falloff
spot_light location, pointed_at
The vector location defines the position of the spot light, the vector
pointed_at defines the point at which the spot light is directed. The
optional components are:
color - The color of the spotlight
Tightness - The power function used to determine the shape of
the hot spot
Angle - The angle (in degrees) of the full effect of the
spot light
Falloff - A larger angle at which the amount of light falls
to nothing
A sample declaration is:
spot_light white, <10,10,0>, <3,0,0>, 3, 5, 20
56
Sample file: spot0.pi
2.4.1.3 Directional lights
The directional light means just that - light coming from some
direction.
directional_light color, direction
directional_light direction
An example would be: directional_light <2, 3, -4>, giving a white
light coming from the right, above, and behind the origin.
2.4.1.4 Textured lights
Textured lights are an enhancement of point lights that add: a
function (including image maps) to describe the intensity & color of
the light in each direction, transformations, and size. The format of
the declaration is:
textured_light {
color float
[sphere center, radius]
[translate/rotate/scale]
}
Any color expression is allowed for the textured light, and is
evaluated at run time.
A rotating slide projector light from the data file ilight.pi is shown
below:
define block_environ
environment("one.tga", "two.tga", "three.tga",
"four.tga", "five.tga", "six.tga")
textured_light {
color environment_map(P, block_environ)
rotate <frame*6, frame*3, 0>
translate <0, 2, 0>
}
Sample files: environ.pi, ilight.pi
2.4.1.4.1 Area Lights
By adding a sphere declaration to a textured_light, it is turned into
an area light. The current implementation is a bit rough for long and
narrow shadows, but in general gives very good results. A typical
declaration of an area light is:
textured_light {
57
color white
sphere <8, 10, 0>, 1
}
Sample file: spoty.pi
2.4.1.5 Depth Mapped Lights
Depth mapped lights are very similar to spotlights, in the sense that
they point from one location and at another location. The primary use
for this light type is for doing shadowing in scan converted scenes.
The format of their declaration is:
depthmapped_light {
[ angle float ]
[ aspect float ]
[ at vector ]
[ color expression ]
[ depth "depthfile.tga" ]
[ from vector ]
[ hither float ]
[ up vector ]
}
You may notice that the format of the declaration is very similar to
the viewpoint declaration. This is intentional, as you will usually
generate the depth information for depthfile.tga as the output of a
run of Polyray. To support output of depth information, a new
statements was added to the viewpoint declaration, image_format.
A viewpoint declaration that will output a depth file would have the
form:
viewpoint {
from [ location of depth mapped light ]
at [ location the light is pointed at ]
image_format 1
}
Where the final statement tells Polyray to output depth information
instead of color information. Note that if the value in the
image_format statement is 0, then normal rendering will occur.
If a hither declaration is used, then the value given is used as a
bias to help prevent self shadowing. The default value for this bias
is the value of shadow_tolerance in polyray.ini.
Sample File: room1.pi, depmap.pi
2.4.2 Background color
58
The background color is the one used if the current ray does not
strike any objects. The color can be any vector expression, although
is usually a simple RGB color value.
The format of the declaration is:
background <R,G,B>
or
background color
If no background color is set black will be used.
An interesting trick that can be performed with the background is to
use an image map as the background color (it is also a way to
translate from one Targa format to another). The way this can be done
is:
background planar_imagemap(image("test1.tga", P)
The background also affects the opacity channel in 16 and 32 bit Targa
output images. If any background contributes directly to the pixel
(not as a result of reflection or refraction), then the attribute bit
is cleared in a 16 bit color and the attribute byte is set to the
percentage of the pixel that was covered by the background in a 32 bit
color.
In order to extend the flexibility of the background, the meanings of
various runtime variables are set uniquely.
u,x - How far across the output image (from 0 at left to 1 at
right)
v,z - How far down the output image (from 0 at bottom to 1 at top)
W - <0,0,0>
P - Same as <x, 0, z>
N - Direction of the current ray
I - <0,0,0>
U - Same as <u, v, w>
w - Level of recursion (0 for eye rays, higher for reflected
and refracted rays)
As an example, suppose you wanted to have an image appear in the
background, but you didn't want to have the image appear in any
reflections. Then you could define the background with the following
expression:
background (w == 0 ? planar_imagemap(img1, P) : black)
If you wanted to have one image in the background, and another that
appears in reflections (or refracted rays), then you could use the
following expression:
background (w == 0 ? planar_imagemap(img1, P)
: spherical_imagemap(img2, N))
59
The previous background expression fits img1 exactly into the output
imgae and maps img2 completely around the entire scene. This might be
useful if you have a map of stars that you want to appear in
reflections, but still want to have an image as the background.
2.4.2.1 Global Haze (fog)
The global haze is a color that is added based on how far the ray
traveled before hitting the surface. The format of the expression is:
haze coeff, starting_distance, color
The color you use should almost always be the same as the background
color. The only time it would be different is if you are trying to put
haze into a valley, with a clear sky above (this is a tough trick, but
looks nice). A example would be:
haze 0.8, 3, midnight_blue
The value of the coeff ranges from 0 to 1, with values closer to 0
causing the haze to thicken, and values closer to 1 causing the haze
to thin out. I know it seems backwards, but it is working and I don't
want to break anything.
2.4.3 Textures
Polyray supports a few simple procedural textures: a standard shading
model, a checker texture, a hexagon texture, and a general purpose (3D
noise based) texture. In addition, a very flexible (although slower)
functional texture is supported. Individual textures can be combined
in various ways to create new ones. Texture types that help to
combine other textures include: layered textures, indexed textures,
and summed textures. (See section 2.4.3.3 through 2.4.3.5).
The general syntax of a texture is:
texture { [texture declaration] }
or
texture_sym
Where texture_sym is a previously defined texture declaration.
2.4.3.1 Procedural Textures
Procedural textures (i.e. checker, matte_white, shiny_red, ...) are
ones that are completely defined at the time the data file is read.
Textures that are evaluated during rendering are described in section
2.4.3.2.
60
2.4.3.1.1 Standard Shading Model
Unlike many other ray-tracers, surfaces in Polyray not have a single
color that is used for all of the components of the shading model.
Instead a number of characteristics of the surface must be defined
(with a matte white being the default).
A surface declaration has the form:
surface {
[ surface definitions ]
}
For example, the following declaration is a red surface with a white
highlight, corresponding to the often seen plastic texture:
define shiny_red
texture {
surface {
ambient red, 0.2
diffuse red, 0.6
specular white, 0.8
microfacet Reitz 10
}
}
The allowed surface characteristics that can be defined are:
color - Color if not given in another component
ambient - Light given off by the surface
diffuse - Light reflected in all directions
specular - Amount and color of specular highlights
reflection - Reflectivity of the surface
transmission - Amount and color of refracted light
microfacet - Specular lighting model (see below)
The lighting equation used is (in somewhat simplified terms):
L = ambient + diffuse + specular + reflected + transmitted, or
L = Ka + Kd * (l1 + l2 + ...) + Ks * (l1 + l2 + ...) + Kr + Kt
Where l1, l2, ... are the lights, Ka is the ambient term, Kd is the
diffuse term, Ks is the specular term, Kr is the reflective term, and
Kt it the transmitted (refractive) term. Each of these terms has a
scale value and a filter value (the filter defaults to white/clear if
unspecified).
See the file colors.inc for a number of declarations of surface
characteristics, including: mirror, glass, shiny, and matte.
For lots of detail on lighting models, and the theory behind how color
is used in computer generated images, run (don't walk) down to your
local computer bookstore and get:
61
Illumination and Color in Computer Generated Imagery
Roy Hall, 1989
Springer Verlag
Source code in the back of that book was the inspiration for the
microfacet distribution models implemented for Polyray.
Note that you don't really have to specify all of the color components
if you don't want to. If the color of a particular part of the
surface declaration is not defined, then the value of the color
component will be examined to see if it was declared. If so, then
that color will be used as the filter. As an example, the declaration
above could also be written as:
define shiny_red
texture {
surface {
color red
ambient 0.2
diffuse 0.6
specular white, 0.8
microfacet Reitz 10
}
}
2.4.3.1.1.1 Ambient light
Ambient lighting is the light given off by the surface itself. This
will be a constant amount, independent of any lights that may be in
the scene.
The format of the declaration is:
ambient color, scale
ambient scale
As always, color indicates either an RGB triple like <1.0,0.7,0.9>, or
a named color. scale gives the amount of contribution that ambient
gives to the overall amount light coming from the pixel. The scale
values should lie in the range 0.0 -> 1.0
2.4.3.1.1.2 Diffuse light
Diffuse lighting is the light given off by the surface under
stimulation by a light source. The intensity of the diffuse light is
directly proportional to the angle of the surface with respect to the
light.
The format of the declaration is:
diffuse color, scale
62
diffuse scale
The only information used for diffuse calculations is the angle of
incidence of the light on the surface.
2.4.3.1.1.3 Specular highlights
The format of the declaration is:
specular color, scale
specular scale
The means of calculating specular highlights is by default the Phong
model. Other models are selected through the Microfacet distribution
declaration.
2.4.3.1.1.4 Reflected light
Reflected light is the color of whatever lies in the reflected
direction as calculated by the relationship of the view angle and the
normal to the surface.
The format of the declaration is:
reflection scale
reflection color, scale
Typically, only the scale factor is included in the reflection
declaration, this corresponds to all colors being reflected with
intensity proportional to the scale. A color filter is allowed in the
reflection definition, and this allows the modification of the color
being reflected (I'm not sure if this is useful, but I included it
anyway).
2.4.3.1.1.5 Transmitted light
Transmitted light is the color of whatever lies in the refracted
direction as calculated by the relationship of the view angle, the
normal to the surface, and the index of refraction of the material.
The format of the declaration is:
transmit scale, ior
transmit color, scale, ior
Typically, only the scale factor is included in the transmitted
declaration, this corresponds to all colors being transmitted with
intensity proportional to the scale. A color filter is allowed in the
transmitted definition, and this allows the modification of the color
being transmitted by making the transmission filter different from the
color of the surface itself.
63
It is possible to have surfaces with colors very different than the
one closest to the eye become apparent. (See gsphere.pi for an
example, a red sphere is in the foreground, a green sphere and a blue
sphere behind. The specular highlights of the red sphere go to
yellow, and blue light is transmitted through the red sphere.)
A more complex file is lens.pi in which two convex lenses are lined up
in front of the viewer. The magnified view of part of a grid of
colored balls is very apparent in the front lens.
2.4.3.1.1.6 Microfacet distribution
The microfacet distribution is a function that determines how the
specular highlighting is calculated for an object.
The format of the declaration is:
microfacet Distribution_name falloff_angle
microfacet falloff_angle
The distribution name is one of: Blinn, Cook, Gaussian, Phong, Reitz.
The falloff angle is the angle at which the specular highlight falls
to 50% of its maximum intensity. (The smaller the falloff angle, the
sharper the highlight.) If a microfacet name is not given, then the
Phong model is
used.
The falloff angle must be specified in degrees, with values in the
range 0 to 45. The falloff angle corresponds to the roughness of the
surface, the smaller the angle, the smoother the surface.
Note: as stated before, look at the book by Hall. I have found
falloff values of 5-10 degrees to give nice tight highlights. Using
falloff angle may seem a bit backwards from other raytracers, which
typically use the power of a cosine function to define highlight size.
When using a power value, the higher the power, the smaller the
highlight. Using angles seems a little tidier since the smaller the
angle, the smaller the highlight.
2.4.3.1.2 Checker
the checker texture has the form:
texture {
checker texture1, texture2
}
where texture1 and texture2 are texture declarations (or texture
constants).
A standard sort of checkered plane can be defined with the following:
64
// Define a matte red surface
define matte_red
texture {
surface {
ambient red, 0.1
diffuse red, 0.5
}
}
// Define a matte blue surface
define matte_blue
texture {
surface {
ambient blue, 0.2
diffuse blue, 0.8
}
}
// Define a plane that has red and blue checkers
object {
disc <0, 0.01, 0>, <0, 1, 0>, 5000
texture {
checker matte_red, matte_blue
}
}
For a sample file, look at spot0.pi. This file has a sphere with a
red/blue checker, and a plane with a black/white checker.
2.4.3.1.3 Hexagon
the hexagon texture is oriented in the x-z plane, and has the form:
texture {
hexagon texture1, texture2, texture3
}
This texture produces a honeycomb tiling of the three textures in the
declaration. Remember that this tiling is with respect to the x-z
plane, if you want it on a vertical wall you will need to rotate the
texture.
2.4.3.1.4 Noise surfaces
The complexity and speed of rendering of the noise surface type lies
between the standard shading model and the special surfaces described
below. It is an attempt to capture a large number of the standard 3D
texturing operations in a single declaration.
A noise surface declaration has the form:
texture {
noise surface {
65
[ noise surface definition ]
}
}
The allowed surface characteristics that can be defined are:
color <r, g, b> - Basic surface color (used if the noise
function generates a value not contained
in the color map)
ambient scale - Amount of ambient contribution
diffuse scale - Diffuse contribution
specular color, scale - Amount and color of specular highlights,
if the color is not given then the body
color will be used.
reflection - Reflectivity of the surface
transmission scale, ior - Amount of refracted light
microfacet kind angle - Specular lighting model (see the
description of a standard surface)
color_map(map_entries) - Define the color map (see following
section on color map definitions for
further details)
bump_scale float - How much the bumps affect the normal
frequency float - Affects the wavelength of ripples and
waves
phase float - Affects the phase of ripples and waves
lookup_fn index - Selects a predefined lookup function
normal_fn index - Selects a predefined normal modifier
octaves float - Number of octaves of noise to use
position_fn index - How the intersection point is used in
the generation of a noise texture
position_scale float - Amount of contribution of the position
value to the overall texture
turbulence float - Amount of contribution of the noise to
overall texture.
The way the final color of the texture is decided is by calculating a
floating point value using the following general formula:
index = lookup_fn(position_scale * position_fn +
turbulence * noise3d(P, octaves))
The index value that is calculated is then used to lookup a color from
the color map. This final color is used for the ambient, diffuse,
reflection and transmission filters. The functions that are currently
available, with their corresponding indices are:
Position functions:
Index Effect
1 x value in the object coordinate system
2 x value in the world coordinate system
3 Distance from the z axis
4 Distance from the origin
5 Angle from the x-axis (in the x-z plane, from 0 to 1)
66
default: 0.0
Lookup functions:
Index Effect
1 sawtooth function, result from 0 -> 1
2 sin function, result from 0->1
3 ramp function, result from 0->1
default: no modification made
Definitions of these function numbers that make sense are:
define position_plain 0
define position_objectx 1
define position_worldx 2
define position_cylindrical 3
define position_spherical 4
define position_radial 5
define lookup_plain 0
define lookup_sawtooth 1
define lookup_sin 2
define lookup_ramp 3
An example of a texture defined this way is a simple white marble:
define white_marble_texture
texture {
noise surface {
color white
position_fn position_objectx
lookup_fn lookup_sawtooth
octaves 3
turbulence 3
ambient 0.2
diffuse 0.8
specular 0.3
microfacet Reitz 5
color_map(
[0.0, 0.8, <1, 1, 1>, <0.6, 0.6, 0.6>]
[0.8, 1.0, <0.6, 0.6, 0.6>, <0.1, 0.1, 0.1>])
}
}
In addition to coloration, the bumpiness of the surface can be
affected by selecting a function to modify the normal. The currently
supported normal modifier functions are:
Index Effect
1 Make random bumps in the surface
2 Add ripples to the surface
3 Give the surface a dented appearance
default: no change
Definitions that make sense are:
67
define default_normal 0
define bump_normal 1
define ripple_normal 2
define dented_normal 3
See also the file texture.txt for a little more explanation.
2.4.3.2 Functional Textures
The most general and flexible texture type is the functional texture.
These textures are evaluated at run-time based on the expressions
given for the components of the lighting model. The general syntax
for a surface using a functional texture is:
special surface {
[ surface declaration ]
}
In addition to the components usually defined in a surface
declaration, it is possible to define a function that deflects the
normal, and a function that modifies the intersection point prior to
texturing. The format of the two declarations are:
position vector
normal vector
An example of how a functional texture can be defined is:
define sin_color_offset (sin(3.14 * fmod(x*y*z,1) + otheta)+1)/2
define sin_color <sin_color_offset, 0, 1 - sin_color_offset>
define xyz_sin_texture
texture {
special surface {
color sin_color
ambient 0.2
diffuse 0.7
specular white, 0.2
microfacet Reitz 10
}
}
In this example, the color of the surface is defined based on the
location of the intersection point using the vector defined as
sin_color. Note that sin_color uses yet another definition.
The position declaration is useful to achieve the effect of
turbulence. By adding a solid noise to the position, it is possible
to add swirl to a basic texture. For example:
define white_marble
68
texture {
special shiny {
color white_marble_map[sawtooth(x)]
position P + dnoise(P, 3)
}
This will create a basic white marble (which would have very straight,
even bands of color), and by adding dnoise, swirls the colors around.
The normal declaration is used to add some bumpyness to the surface.
The bumps can be created with a statement as simple as:
normal N + (dnoise(P) - <0.5, 0.5, 0.5>)
The value <0.5,0.5,0.5> is subtracted from the dnoise function since
dnoise only returns positive values in each component.
Note: if the color component has any alpha in it (as the result of a
lookup from a color map or from an image) then that alpha value will
be used as the scale for the transmit statement.
Sample files: cossph.pi, cwheel.pi.
2.4.3.2.1 Color maps
Color maps are generally used in noise textures and functional
textures. They are a way of representing a spectrum of colors that
blend from one into another. Each color is represented as RGB, with
an optional alpha (transparency) value. The format of the declaration
is:
color_map([low0, high0, <r0, g0, b0>, a0, <r1, g1, b1>, a1]
[low1, high1, <r2, g2, b2>, a2, <r3, g3, b3>, a3]
...
[lowx, highx, <rx, gx, bx>, ax, <ry, gy, by>, ay])
Note that there are no commas between entries in the color map even
though commas are required within each entry. (This is a holdover to
retain compatibility with earlier versions of Polyray.) If you don't
need any transparency in the color map, then the following declaration
could be used:
color_map([low0, high0, <r0, g0, b0>, <r1, g1, b1>]
[low1, high1, <r2, g2, b2>, <r3, g3, b3>]
...
[lowx, highx, <rx, gx, bx>, <ry, gy, by>])
In this case, the alpha is set to 0 for all colors created by the
color map. Note that it is possible to mix colors with and without
alpha value.
Note: If there is an alpha value in the color map, then the amount of
alpha is used as the scale for the transmit component of the surface.
69
To turn off this automatic transparency, use transmit 0.
2.4.3.2.1.1 Using CMAPPER
A good way to build color maps for layered textures is with
ColorMapper,
Written by : SoftTronics, Lutz + Kretzschmar
This is available as CMAP.ZIP in the forum Graphdev on Compuserve.
This program allows you to build color maps with varying colors and
transparency values. The output of this program does have to be
massaged a little bit to make it into a color map as Polyray
understands it. In order to help with this process an IBM executable,
makemap.exe has been included. To use this little program, you follow
these steps:
1) run CMAPPER to create a color map in the standard output (not
the POV-Ray output format).
2) run makemap on that file, giving a name for the new Polyray
color map definition
3) Add this definition to your Polyray data file.
If you saved your map as foo.map, and you wanted to add this color map
to the Polyray data file foo.inc, with the name of foox_map, you would
then run makemap the following way:
makemap foo.map foox_map >> foo.inc
This makes the translation from CMAPPER format to Polyray format, and
appends the output as, define foox_map color_map(...), to the file
foo.inc.
2.4.3.2.2 Image maps
Projecting an image onto a surface is one of the most common texturing
techniques. There are four types of projection supported: planar,
cylindrical, spherical, and environment. Input files for use as image
maps may be any valid Targa, GIF, or JPEG image.
The declaration of an image map is:
image("imfile.tga")
Typically, an image will be associated with a variable through a
definition such as:
define myimage image("imfile.tga")
The image is projected onto a shape by means of a projection. The
four types of projection are declared by:
70
planar_imagemap(image, coordinate [, repeat]),
cylindrical_imagemap(image, coordinate [, repeat]),
spherical_imagemap(image, coordinate)
environment_map(coordinate,
environment("img1.tga", "img2.tga", "img3.tga",
"img4.tga", "img5.tga", "img6.tga"))
The planar projection maps the entire raster image into the
coordinates 0 <= x <= 1, 0 <= z <= 1. The vector value given as
coordinate is used to select a color by multiplying the x value by the
number of columns, and the z value by the number of rows. The color
appearing at that position in the raster will then be returned as the
result. If a repeat value is given then entire surface, repeating
between every integer Value of x and/or z.
The planar image map is also used for wrapping images based on u/v
coordinates. By using the vector <u, 0, v>, the planar image map will
automatically wrap the image around surfaces that have natural u/v
coordinates (such as sphere, cylinder, torus, etc.).
The cylindrical projection wraps the image about a cylinder that has
one end at the origin and the other at <0, 1, 0>. If a repeat value
is given, then the image will be repeated along the y-axis, if none is
given, then any part of the object that is not covered by the image
will be given the color of pixel (0, 0).
The spherical projection wraps the image about an origin centered
sphere. The top and bottom seam are folded into the north and south
poles respectively. The left and right edges are brought together on
the positive x axis.
The environment map wraps six images around a point. This method is a
standard way to fake reflections by wrapping the images that would be
seen from a point inside an object around the object. A sample of
this technique can be seen by rendering room1.pi (which generates the
images) followed by rendering room0.pi (which wraps the images around
a sphere).
Following are a couple of examples of objects and textures that make
use of image maps:
define hivolt_image image("hivolt.tga")
define hivolt_tex
texture {
special surface {
color cylindrical_imagemap(hivolt_image, P, 1)
ambient 0.9
diffuse 0.1
}
scale <1, 2, 1>
translate <0, -1, 0>
}
object { cylinder <0, -1, 0>, <0, 1, 0>, 3 hivolt_tex }
71
and
define disc_image image("test.tga")
define disc_tex
texture {
special surface {
color planar_imagemap(disc_image, P)
ambient 0.9
diffuse 0.1
}
translate <-0.5, 0, -0.5>
scale <7*4/3, 1, 7>
rotate <90, 0, 0>
}
object {
disc <0, 0, 0>, <0, 0, 1>, 6
u_steps 10
disc_tex
}
Note: If there is an alpha/opacity value in the image map, then the
amount of opacity is used as the scale for the transmit component of
the surface. To turn off this automatic transparency, use transmit 0.
Sample files: map0.pi, map1.pi, map2.pi
2.4.3.2.3 Bumpmaps
Bumpmaps are declared using the same sort of projections as image maps
(excepting environment maps). The following are the valid
declarations of bump maps:
planar_bumpmap(image, coordinate [, bump size]),
cylindrical_bumpmap(image, coordinate [, bump size]),
spherical_bumpmap(image, coordinate [, bump size])
Instead of an optional repeat argument, bumpmaps have an optional bump
size argument. If this argument is left out, then the bump size is
set to one. Note that negative bump values are allowed and cause the
bumps to appear to project the other way.
Any Targa image can be used, but for best results greyscale or color
mapped images are best. The following declarations show how a bump
map can be applied to objects:
include "colors.inc"
define tile_bumps image("tile1.tga")
define bumpmap_red1
texture {
special shiny {
72
color red
normal planar_bumpmap(tile_bumps, <8*u, 0, 6*v>, 1)
}
}
object {
object { torus 2, 0.75, <0, -1.25, 0>, <0, 1, 0> }
+ object { cone <0, -2, 0>, 1, <0, 3, 0>, 0 }
+ object { sphere <2, 0, 4>, 2 }
bumpmap_red1
}
The bumpmap is declared using u/v coordinates so that it will follow
the natural coordinates of the object. This ensures that it wraps
properly around the torus, cone, and sphere in the example above.
There is an automatic wrapping of bump maps, so there will be 8 tiles
in the u direction and 6 tiles in the v direction of each object.
Sample file:
2.4.3.3 Indexed Textures and Texture Maps
A texture map is declared in a manner similar to color maps. There is
a list of value pairs and texture pairs, for example:
define index_tex_map
texture_map([-2, 0, red_blue_check, bumpy_green],
[0, 2, bumpy_green, reflective_blue])
Note that for texture maps there is a required comma separating each
of the entries.
These texture maps are complimentary to the indexed texture (see
below). Two typical uses of indexed textures are to use solid
texturing functions to select (and optionally blend) between complete
textures rather than just colors, and to use image maps as a way to
map textures to a surface.
For example, using the texture map above on a sphere can be done
accomplished with the following:
object {
sphere <0, 0, 0>, 2
texture { indexed x, index_tex_map }
}
The indexed texture uses a lookup function (in example above a simple
gradient along the x axis) to select from the texture map that
follows. See the data file indexed1.pi for the complete example.
As an example of using an image map to place textures on a surface,
the following example uses several textures, selected by the color
values in an image map. The function indexed_map returns the color
index value from a color mapped image (or uses the red channel in a
73
raw image). The example below is equivalent to creating a material
map in the POV-Ray raytracer.
object {
sphere <0, 0, 0>, 1
texture {
indexed indexed_map(image("txmap.tga"), <x, 0, y>, 1),
texture_map([1, 1, mirror, mirror],
[2, 2, bright_pink, bright_pink],
[3, 3, Jade, Jade])
translate <-0.5, -0.5, 0> // center image
}
}
In this example, the image is oriented in the x-y plane and centered
on the origin. The only difference between a indexed_map and a
planar_imagemap is that the first (indexed_map) returns the index of
the color in the image and the second returns the color itself. Note
that the texture map shown above has holes in it (between the integer
values), however this isn't a problem as the indexed_map function will
only produce integers.
2.4.3.4 Layered Textures
Layered textures allow you to stack multiple textures on top of each
other. If a part of the texture is not completely opaque (non-zero
alpha), then the layers below will show through. For example, the
following texture creates a texture with a marble outer layer and a
mirrored bottom layer and applies it to a sphere:
include "colors.inc"
define marble_alpha_map
color_map([0.0, 0.2, white, 0, white, 0]
[0.2, 0.5, white, 0, black, 0.2]
[0.6, 1.0, black, 0.2, black, 1])
define mirror_veined_marble
texture {
layered
texture {
special shiny { color marble_alpha_map[marble_fn] }
},
mirror
}
object {
sphere <0, 0, 0>, 2
mirror_veined_marble
}
Sample files: Stone1-Stone24, layer1.pi, layer2.pi
74
2.4.3.5 Summed Textures
Summed textures simply add weighted amounts of a number of textures
together to make the final color. The syntax is:
texture {
summed f1, tex1, f2, tex2, ...
}
The expressions f1, f2, ... are numeric expressions. The expressions
tex1, ... are textures.
Sample file: blobtx.pi
2.5 Comments
Comments follow the standard C/C++ formats. Multiple line comments
are enclosed by /* ... */ and single line comments are preceeded by
//.
Single line comments are allowed and have the following format:
// [ any text to end of the line ]
As soon as the two characters // are detected, the rest of the line is
considered a comment.
2.6 Animation support
An animation is generated by rendering a series of frames, numbered
from 0 to some total value. The declarations in Polyray that support
the generation of multiple Targa images are:
total_frames val - The total number of frames in the animation
start_frame val - The value of the first frame to be rendered
end_frame val - The last frame to be rendered
frame_time val - Duration of each frame (for particles)
outfile "name" - Polyray appends the frame number to 'name'
outfile name in order to generate distinct Targa files.
The values of total_frames, start_frame, and end_frame, as well as the
value of the current frame, frame, are usable in arithmetic
expressions in the input file. Note that these statements should
appear before the use of: total_frames, start_frame, end_frame, or
frame as a part of an expression. Typically I put the declarations
right at the top of the file.
WARNING: if the string given for outfile is longer than 5 characters,
the three digit frame number that is appended will be truncated by
DOS. Make sure this string is short enough or you will end up
overwriting image files.
75
Sample files: whirl.pi, plane.pi, squish.pi, many others
2.7 Conditional processing
In support of animation generation (and also because I sometimes like
to toggle attributes of objects), polyray supports limited conditional
processing. The syntax for this is:
if (cexper) {
[object/light/... declarations]
}
else {
[other object/light/... declarations]
}
The sample file rsphere.pi shows how it can be used to modify the
color characteristics of a moving sphere.
The use of conditional statements is limited to the top level of the
data file. You cannot put a conditional within an object or texture
declaration. i.e.
object {
if (foo == 42) {
sphere <0, 0, 0>, 4
}
else {
disc <0, 0, 0>, <0, 1, 0>, 4
}
}
is not a valid use of an if statement, whereas:
if (foo == 42) {
object {
sphere <0, 0, 0>, 4
}
}
else {
object {
disc <0, 0, 0>, <0, 1, 0>, 4
}
}
or
if (foo == 42)
object { sphere <0, 0, 0>, 4 }
else if (foo = 12) {
object { torus 3.0, 1.0, <0, 0, 0>, <0, 1, 0> }
object { cylinder <0, -1, 0>, <0, 1, 0>, 0.5 }
}
else
object { disc <0, 0, 0>, <0, 1, 0>, 4 }
76
are valid.
Note: the curly brackets { } are required if there are multiple
statements within the conditional, and not required if there is a
single statement.
2.8 Include files
In order to allow breaking an input file into several files (as well
as supporting the use of library files), it is possible to direct
polyray to process another file. The syntax is:
include "filename"
Beware that trying to use #include ... will fail.
2.9 File flush
Due to unforeseen occurrences, like power outages, or roommates that
hit the reset button so they can do word processing, it is possible to
specify how often the output file will be flushed to disk. The
default is to wait until the entire file has been written before a
flush (which is a little quicker but you lose everything if a crash
occurs).
The format of the declaration is:
file_flush xxx
The number xxx indicates the maximum number of pixels that will be
written before a file flush will occur. This value can be as low as 1
- this will force a flush after every pixel (only for the very
paranoid).
2.10 System calls
The system call allows you to invoke an external program from within
Polyray. This is useful if you have a program that will generate new
data for every frame of an animation. By using system, you can invoke
the external program for every frame of the program, and possibly pass
it the frame number so that it will know the specific data to
generate.
The format of the declaration is:
system(arg1, ..., argn)
The arguments are concatenated together (no spaces added) and then
passed to the system to execute. Any numeric or string variable is
allowed for an argument. The number of arguments isn't limited,
however the total number of characters in the final string must be
less than 255.
77
One possible use for the system call is to use DTA to extract a
particular frame of an animation for use in an image map. By
incrementing the frame number that is retrieved, you can embed an
animation into your own animation.
3 File formats
3.1 Output files
Currently the output file formats are 8, 16, 24, and 32 bit
uncompressed and RLE compressed Targa. If no output file is defined
with the '-o' command line option, the file out.tga will be used. The
command line option -u specifies that no compression should be used on
the output file.
The default output format is an RLE compressed 16 bit color format.
This format holds 5 bits for each of red, green, and blue. If you
have the hardware to display more than 32K colors then set the command
line switches (i.e. -p 24 or -p 32) or set the defaults in the
polyray.ini (i.e. pixelsize 24 or pixelsize 32) to generate 24 or 32
bit Targa files.
4 Outstanding Issues
Polyray will probably never be done. And in this respect, the next
two sections list some things that are planned for the future, as well
as things that should already have been fixed.
4.1 To do list
Add defined color_maps to noise surfaces. Currently you have to
put the entire color map into the noise surface declaration.
Parameterised objects and textures. This will occur in the next
major release of Polyray, due to the extreme structural changes
required.
User definable positioning of display elements, in particular
placement of the image as it is drawn, and placement of the status
display during the trace.
Appending .pi to input file names that do not have an extension.
Use of environment variables to find polyray.ini and standard
include ciles.
Trim curves for NURBS
4.2 Known Bugs
It is possible to specify surface components that have no meaning for
the type of surface that is being declared (e.g., octaves in a special
surface).
78
The initialization file polyray.ini has to be in the current
directory.
Shading in scan conversion doesn't always match that from raytracing.
Not much can be done about this other than increasing the values of
u_steps and v_steps.
Refraction does not appear to be correct when the ray passes through
several overlapping transparent objects. Works fine if the ior is 1.0,
or if there is only 1 object intersected by the ray. The cure is to
use clipping or u/v bounds to remove the overlapping pieces.
5 Revision History
Version 1.7
Released: 17 March 1994
o Antialiasing improved. Levels of detail are now given rather
than numbers of random samples. Additionally, the uniform
subdivision provides consistency across across frames of
an animation.
o Added parametric surfaces. Now possible to define a mesh
object (surface) where each point in the mesh is a function
of u and v.
o Added bump maps
o Added noeval to definitions to cure a particle system bug
o Added support for GIF and JPEG images in textures, etc.
o Added support for system calls in 386 version. Can now
call an external program from within Polyray.
o The default shading flags for raytracing is now set to:
SHADOW_CHECK + REFLECT_CHECK + TRANSMIT_CHECK +
UV_CHECK + CAST_SHADOW
The difference is that it is no longer assumed that surfaces
should be lit on both sides (normal correction), this boosts
rendering speed at the cost of funny shading once in a while.
UV_CHECK was added to allow turning off checking of u/v bounds
on objects (also a speedup).
o Changed u_steps, v_steps back to the way it was in v1.5. Now
for all objects it is uniformly subdivided by exactly the
value of u_steps/v_steps.
o Significantly enhanced the function object. Polyray will
now accept any predefined function as part of the function
definition.
o Added NURBS objects. (Sorry, no trim curves yet.)
o Displacement functions are now possible on CSG objects. They
also work in raytracing now (although you may need to really
boost the u_steps and v_steps values to get good results).
79
o Now supports /* ... */ comments (C style). No you can't nest
them. You can nest the single line // comments within them.
o Internal modifications made that should result in less memory
usage by objects. Your mileage may vary.
o Modified numeric input to allow numbers like 1., .1
o Fixed up bug in polygon tracing that caused them to drop out
if they were oriented in just the right way.
o Added a second raw triangle output format - only outputs the
vertex coordinates. This makes it more compatible with
RAW2POV. Previous style with normals and u/v still available.
o Removed BSP tree bounding. It wasn't any faster than slabs
and locked up every once in a while.
o Added a glyph object to support TrueType style extruded
surfaces. Glyphs are always closed at the top and bottom, they
may have both straight and curved sides in the same shape. A
program to extract TrueType information and write into Polyray
glyph format is included with the data file archive.
o Support for simple particle systems. Can define birth and
death conditions, # of objects to generate, and ability to
bounce off other objects.
o Added many more VESA display modes. There are now 5 SVGA 256
color modes, 5 Hicolor modes, and 5 truecolor modes supported.
If your board doesn't support a selected mode, Polyray tries a
lower res one having the same # of bytes per pixel.
Version 1.6a (Crud, stuff was still broken...)
Released: 23 April 1993
o Problems in writing Targa images! Uncompressed/RLE was being
set improperly in the image file.
o Allocation/Deallocation of static variables had some problems.
Mostly fixed, however: DONT use a non-static texture in a
static object.
o Added some code to special surface to make use of an alpha
value in a color map. If you use, color xxx_map[foo_fn], it
will grab the alpha value and use it for the transmission
scale. (This overrides any transmission scale you might put
in later, so beware.)
Version 1.6 (beta)
Released:
o Added opacity values to 16 and 32 bit Targa output. Just a
single bit in the 16 bit files. In the 32 bit files the extra
80
channel holds the percentage of the background that
contributed to the pixel.
o Added static (last from frame to frame) variables
o Added indexed and summed textures.
o Added RLE compressed 8 bit Targa files.
o Added VESA Hicolor display in 640x480 (still a bit buggy).
Don't use anything but '-t 0' or '-t 1' for status displays or
the screen will go wierd after about 50 lines.
o Added texture maps (similar to color maps), indexed image
files (for use with texture maps)
o Added output of raw triangle information. Render type 3 will
render the image as triangles, in ASCII form. Each line of
the output has the form: v1 v2 v3 n1 n2 n3 uv1 uv2 uv3.
Where vx is a 3D vertex, nx is the normal at the corresponding
vertex, and uv1 is a pair of u,v values for each vertex.
(This means there are 24 floating point values per line.)
o Fixed bug involving conditional include files. If an include
directive was inside a conditional, the file was read
regardless of the value of the conditional.
o Smoothed out Bezier surfaces. A patch (smooth) triangle is
now used instead of a flat one when performing final
intersection tests.
o Fixed view bug in scan conversion - if the direction of view
was along the y-axis & the up was along the z-axis, the image
came out upside down.
o Added displacement to scan converted surfaces
o Added u-v mapping to most primitives. The variables u and v
work in all expressions. This is especially useful for image
mapping.
o Added u-v limits to several primitives. This allows for
creation of sections of a primitive.
o Changed scan conversion to be adaptive to the on-screen pixel
size. Unless limited by u_steps or v_steps, the subdivision of
a primitive continues until a polygon about the size of a
pixel is created. This is then drawn. The biggest drawback
is the appearance of cracks when the adaptive subdivision is
different for adjacent parts of a surface.
o Changed the meaning of u_steps and v_steps for most
primitives. They now mean the number of binary subdivisions
that are performed rather than the number of steps. Therefore
81
u_steps 4 in this version is equivalent to u_steps 16 in
previous versions. (Unaffected primitives: blobs, sweeps,
lathes. These still work the old way.)
o Fixed dot product of vectors in expressions.
o Added Legendre polynomials as an expression. Pretty cool for
doing spherical harmonics.
o Added depth mapped lights. Useful if you need to do shadows
of things that can only be scan converted (like displacement
surfaces).
o Fixed problems with using a torus in CSG. (Only appeared in
some versions of v1.5)
Version 1.5
(not released)
o Buggy SVGA support removed. Only standard VGA mode (320x200)
supported.
o Gridded objects added.
o Arrays added
o Components of CSG objects are now properly sorted by bounding
slabs
o User defined bounding slabs removed. Polyray will always use
bounding slabs aligned with the x, y, and z-axes.
o Clipping and bounding objects removed. Clipping is now
performed in CSG, bounding is specified using a bounding_box
declaration.
o Added wireframe display mode.
o Added planar blob types. (Also added toroidal blob types, but
they only appear in scan conversion images due to the extreme
numerical precision needed to raytrace them.)
o Added smooth height fields.
o Fixed shading bug involving transparent objects & multiple
light sources.
o Fixed diffuse lighting from colored lights.
o Changed RLE Targa output so that line boundaries are not
crossed.
Version 1.4
Released: 11 April 1992
82
o Support for many SVGA boards at 640x480 resolution in 256
colors. See documentation for the -V flag. (Note: SVGA
displays only work on the 286 versions.)
o Changed the way the status output is managed. Now requires a
number following the -t flag. Note that line and pixel status
will screw up SVGA displays - drawing goes to the wrong place
starting around line 100. If using SVGA display then either
use no status, or totals.
o Added cylindrical blob components. Changed the syntax for
blobs to accommodate the new type.
o Added lathe surfaces made from either line segments or
quadratic splines.
o Added sweep surfaces made from quadratic splines.
o Height field syntax changed slightly. Non-square height fields
now handled correctly.
o Added adaptive antialiasing.
o Squashed bug in shading routines that affected almost all
primitives. This bug was most noticeable for objects that were
scaled using different values for x, y, and z.
o Added transparency values to color maps.
o Added new keywords to the file polyray.ini: shadow_tolerance,
antialias, alias_threshold, max_samples. Lines that begin with
// in polyray.ini are now treated as comments.
o Short document called texture.txt is now included in
PLYDOC. This describes in a little more detail how to go
about developing solid textures using Polyray.
o Added command line argument -z start_line. This allows the
user to start a trace somewhere in the middle of an image.
Note that an image that was started this way cannot be
correctly resumed & completed. (You may be able to use image
cut and paste utilities though.)
Version 1.3
(not released)
o Added support for scan converting implicit functions and
polynomial surfaces using the marching cubes algorithm. This
technique can be slow, and is restricted to objects that have
user defined bounding shapes, but now Polyray is able to scan
convert any primitive.
o A global shading flag has been added in order to selectively
83
turn on/off some of the more time consuming shading options.
This option will also allow for the use of raytracing as a way
of determining shadows, reflectivity, and transparency during
scan conversion.
o Added new keywords to the file polyray.ini: pixel_size,
pixel_encoding, shade_flags.
o Improved refraction code to (mostly) handle transparent
surfaces that are defined by CSG intersection.
o Fixed discoloring of shadows that receive some light through a
transparent object.
o Jittered antialiasing was not being called when the option was
selected, this has been fixed.
o Fixed parsing of blobs and polygons that had large numbers of
entries. Previously the parser would fail after 50-60 elements
in a blob and the same number of vertices of a polygon.
o In keeping with the format used by POV-Ray and Vivid, comments
may now start with // as well as #. The use of the pound
symbol for comments may be phased out in future versions.
Version 1.2
Released: 16 February 1992
o Scan conversion of many primitives, using Z-Buffer techniques.
o New primitives: sweep surface, torus
o Support for the standard 320x200 VGA display in 256 colors.
o An initialization file, polyray.ini, is read before
processing. This allows greater flexibility in tuning many
of the default values used by Polyray.
o User defined bounding slabs added. This greatly improves speed
of rendering on data files with many small objects.
o Noise surface added.
o Symbol table routines completely reworked. Improved speed for
data files containing many definitions.
o Bug in the texturing of height fields corrected.
Version 1.1
(not released)
o Added parabola primitive
o Dithering of rays, and objects
84
o Blob code improved, shading corrected, intersection code is
faster and returns fewer incorrect results.
Version 1.0
Released: 27 December 1991
o Several changes in input syntax were made, the most notable
result being that commas are required in many more places. The
reason for this is that due to the very flexible nature of
expressions possible, a certain amount of syntactic sugar is
required to remove ambiguities from the parser.
o Several new primitives were added: boxes, cones, cylinders,
discs, height fields, and Bezier patches.
o A new way of doing textures was added - each component of the
lighting model can be specified by an implicit function that is
evaluated at run time. Using this feature leads to slower
textures, however because the textures are defined in the data
file instead of within Polyray, development of mathematical
texturing can be developed without making alterations to
Polyray.
o File flush commands in the data file and at the command line
were added.
o Several new Targa variants were added.
o Image mapping added.
o Numerous bug fixes have occurred.
Version 0.3 (beta)
Released: 14 October 1991
o This release added Constructive Solid Geometry, functional
surfaces defined in terms of transcendental functions, a
checker texture, and compressed Targa output.
o Polyray no longer accepted a list of bounding/clipping objects,
only a single object is allowed. since CSG can be used to
define complex shapes, this is not a limitation, and even
better makes for cleaner data files.
Version 0.2 (beta)
(not released)
o This release added animation support, defined objects,
arithmetic expression parsing, and blobs.
Version 0.1 (beta)
(not released)
85
o First incarnation of Polyray. This version had code for
polynomial equations and some of the basic surface types
contained in mtv.